| 
									
										
										
										
											2022-11-17 17:06:07 -08:00
										 |  |  | """Generate the main interpreter switch.
 | 
					
						
							|  |  |  | Reads the instruction definitions from bytecodes.c. | 
					
						
							|  |  |  | Writes the cases to generated_cases.c.h, which is #included in ceval.c. | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											2022-11-02 21:31:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import argparse | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  | import contextlib | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | import itertools | 
					
						
							| 
									
										
										
										
											2022-11-04 17:40:43 -07:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2023-02-20 14:56:48 +00:00
										 |  |  | import posixpath | 
					
						
							| 
									
										
										
										
											2022-11-02 21:31:26 -07:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2023-08-21 19:15:52 +02:00
										 |  |  | import textwrap | 
					
						
							| 
									
										
										
										
											2022-11-17 17:06:07 -08:00
										 |  |  | import typing | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  | from collections.abc import Iterator | 
					
						
							| 
									
										
										
										
											2022-11-02 21:31:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  | import stacking  # Early import to avoid circular import | 
					
						
							| 
									
										
										
										
											2023-08-21 00:40:41 +01:00
										 |  |  | from _typing_backports import assert_never | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  | from analysis import Analyzer | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  | from formatting import Formatter, list_effect_size | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  | from flags import InstructionFlags, variable_used | 
					
						
							|  |  |  | from instructions import ( | 
					
						
							|  |  |  |     AnyInstruction, | 
					
						
							| 
									
										
										
										
											2023-08-16 02:04:17 +08:00
										 |  |  |     AbstractInstruction, | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     Component, | 
					
						
							|  |  |  |     Instruction, | 
					
						
							|  |  |  |     MacroInstruction, | 
					
						
							|  |  |  |     MacroParts, | 
					
						
							|  |  |  |     PseudoInstruction, | 
					
						
							| 
									
										
										
										
											2023-08-16 16:26:43 -07:00
										 |  |  |     TIER_ONE, | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     TIER_TWO, | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | import parsing | 
					
						
							|  |  |  | from parsing import StackEffect | 
					
						
							| 
									
										
										
										
											2022-11-02 21:31:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-20 16:08:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 15:59:19 -08:00
										 |  |  | HERE = os.path.dirname(__file__) | 
					
						
							|  |  |  | ROOT = os.path.join(HERE, "../..") | 
					
						
							| 
									
										
										
										
											2023-02-20 14:56:48 +00:00
										 |  |  | THIS = os.path.relpath(__file__, ROOT).replace(os.path.sep, posixpath.sep) | 
					
						
							| 
									
										
										
										
											2023-01-17 15:59:19 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | DEFAULT_INPUT = os.path.relpath(os.path.join(ROOT, "Python/bytecodes.c")) | 
					
						
							|  |  |  | DEFAULT_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases.c.h")) | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | DEFAULT_OPCODE_IDS_H_OUTPUT = os.path.relpath( | 
					
						
							|  |  |  |     os.path.join(ROOT, "Include/opcode_ids.h") | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | DEFAULT_OPCODE_TARGETS_H_OUTPUT = os.path.relpath( | 
					
						
							|  |  |  |     os.path.join(ROOT, "Python/opcode_targets.h") | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  | DEFAULT_METADATA_OUTPUT = os.path.relpath( | 
					
						
							| 
									
										
										
										
											2023-07-12 11:30:25 +01:00
										 |  |  |     os.path.join(ROOT, "Include/internal/pycore_opcode_metadata.h") | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  | DEFAULT_PYMETADATA_OUTPUT = os.path.relpath( | 
					
						
							|  |  |  |     os.path.join(ROOT, "Lib/_opcode_metadata.py") | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  | DEFAULT_EXECUTOR_OUTPUT = os.path.relpath( | 
					
						
							|  |  |  |     os.path.join(ROOT, "Python/executor_cases.c.h") | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-08-16 02:04:17 +08:00
										 |  |  | DEFAULT_ABSTRACT_INTERPRETER_OUTPUT = os.path.relpath( | 
					
						
							|  |  |  |     os.path.join(ROOT, "Python/abstract_interp_cases.c.h") | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2022-11-09 10:50:09 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-07 10:42:10 -07:00
										 |  |  | # Constants used instead of size for macro expansions. | 
					
						
							|  |  |  | # Note: 1, 2, 4 must match actual cache entry sizes. | 
					
						
							|  |  |  | OPARG_SIZES = { | 
					
						
							|  |  |  |     "OPARG_FULL": 0, | 
					
						
							|  |  |  |     "OPARG_CACHE_1": 1, | 
					
						
							|  |  |  |     "OPARG_CACHE_2": 2, | 
					
						
							|  |  |  |     "OPARG_CACHE_4": 4, | 
					
						
							|  |  |  |     "OPARG_TOP": 5, | 
					
						
							|  |  |  |     "OPARG_BOTTOM": 6, | 
					
						
							| 
									
										
										
										
											2023-10-26 14:43:10 +01:00
										 |  |  |     "OPARG_SAVE_RETURN_OFFSET": 7, | 
					
						
							| 
									
										
										
										
											2023-07-07 10:42:10 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  | INSTR_FMT_PREFIX = "INSTR_FMT_" | 
					
						
							| 
									
										
										
										
											2023-06-13 21:42:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 02:04:17 +08:00
										 |  |  | # TODO: generate all these after updating the DSL | 
					
						
							|  |  |  | SPECIALLY_HANDLED_ABSTRACT_INSTR = { | 
					
						
							|  |  |  |     "LOAD_FAST", | 
					
						
							|  |  |  |     "LOAD_FAST_CHECK", | 
					
						
							|  |  |  |     "LOAD_FAST_AND_CLEAR", | 
					
						
							|  |  |  |     "LOAD_CONST", | 
					
						
							|  |  |  |     "STORE_FAST", | 
					
						
							|  |  |  |     "STORE_FAST_MAYBE_NULL", | 
					
						
							|  |  |  |     "COPY", | 
					
						
							|  |  |  |     # Arithmetic | 
					
						
							|  |  |  |     "_BINARY_OP_MULTIPLY_INT", | 
					
						
							|  |  |  |     "_BINARY_OP_ADD_INT", | 
					
						
							|  |  |  |     "_BINARY_OP_SUBTRACT_INT", | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-02 19:57:30 -08:00
										 |  |  | arg_parser = argparse.ArgumentParser( | 
					
						
							|  |  |  |     description="Generate the code for the interpreter switch.", | 
					
						
							|  |  |  |     formatter_class=argparse.ArgumentDefaultsHelpFormatter, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-09-27 15:27:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | arg_parser.add_argument( | 
					
						
							|  |  |  |     "-v", | 
					
						
							|  |  |  |     "--verbose", | 
					
						
							|  |  |  |     help="Print list of non-viable uops and exit", | 
					
						
							|  |  |  |     action="store_true", | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2022-12-02 19:57:30 -08:00
										 |  |  | arg_parser.add_argument( | 
					
						
							|  |  |  |     "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | arg_parser.add_argument( | 
					
						
							|  |  |  |     "-n", | 
					
						
							|  |  |  |     "--opcode_ids_h", | 
					
						
							|  |  |  |     type=str, | 
					
						
							|  |  |  |     help="Header file with opcode number definitions", | 
					
						
							|  |  |  |     default=DEFAULT_OPCODE_IDS_H_OUTPUT, | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | arg_parser.add_argument( | 
					
						
							|  |  |  |     "-t", | 
					
						
							|  |  |  |     "--opcode_targets_h", | 
					
						
							|  |  |  |     type=str, | 
					
						
							|  |  |  |     help="File with opcode targets for computed gotos", | 
					
						
							|  |  |  |     default=DEFAULT_OPCODE_TARGETS_H_OUTPUT, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  | arg_parser.add_argument( | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     "-m", | 
					
						
							|  |  |  |     "--metadata", | 
					
						
							|  |  |  |     type=str, | 
					
						
							|  |  |  |     help="Generated C metadata", | 
					
						
							|  |  |  |     default=DEFAULT_METADATA_OUTPUT, | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | arg_parser.add_argument( | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     "-p", | 
					
						
							|  |  |  |     "--pymetadata", | 
					
						
							|  |  |  |     type=str, | 
					
						
							|  |  |  |     help="Generated Python metadata", | 
					
						
							|  |  |  |     default=DEFAULT_PYMETADATA_OUTPUT, | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-03-15 08:37:36 -07:00
										 |  |  | arg_parser.add_argument( | 
					
						
							|  |  |  |     "-l", "--emit-line-directives", help="Emit #line directives", action="store_true" | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-03-03 20:59:21 -08:00
										 |  |  | arg_parser.add_argument( | 
					
						
							|  |  |  |     "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  | arg_parser.add_argument( | 
					
						
							|  |  |  |     "-e", | 
					
						
							|  |  |  |     "--executor-cases", | 
					
						
							|  |  |  |     type=str, | 
					
						
							|  |  |  |     help="Write executor cases to this file", | 
					
						
							|  |  |  |     default=DEFAULT_EXECUTOR_OUTPUT, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2023-08-16 02:04:17 +08:00
										 |  |  | arg_parser.add_argument( | 
					
						
							|  |  |  |     "-a", | 
					
						
							|  |  |  |     "--abstract-interpreter-cases", | 
					
						
							|  |  |  |     type=str, | 
					
						
							|  |  |  |     help="Write abstract interpreter cases to this file", | 
					
						
							|  |  |  |     default=DEFAULT_ABSTRACT_INTERPRETER_OUTPUT, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2022-11-02 21:31:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  | class Generator(Analyzer): | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |     def get_stack_effect_info( | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |         self, thing: parsing.InstDef | parsing.Macro | parsing.Pseudo | 
					
						
							| 
									
										
										
										
											2023-08-21 00:40:41 +01:00
										 |  |  |     ) -> tuple[AnyInstruction | None, str, str]: | 
					
						
							| 
									
										
										
										
											2023-01-29 17:28:39 -08:00
										 |  |  |         def effect_str(effects: list[StackEffect]) -> str: | 
					
						
							|  |  |  |             n_effect, sym_effect = list_effect_size(effects) | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |             if sym_effect: | 
					
						
							|  |  |  |                 return f"{sym_effect} + {n_effect}" if n_effect else sym_effect | 
					
						
							|  |  |  |             return str(n_effect) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-29 17:28:39 -08:00
										 |  |  |         instr: AnyInstruction | None | 
					
						
							| 
									
										
										
										
											2023-08-25 18:08:29 +01:00
										 |  |  |         popped: str | None = None | 
					
						
							|  |  |  |         pushed: str | None = None | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |         match thing: | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             case parsing.InstDef(): | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |                 instr = self.instrs[thing.name] | 
					
						
							|  |  |  |                 popped = effect_str(instr.input_effects) | 
					
						
							|  |  |  |                 pushed = effect_str(instr.output_effects) | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             case parsing.Macro(): | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |                 instr = self.macro_instrs[thing.name] | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  |                 popped, pushed = stacking.get_stack_effect_info_for_macro(instr) | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             case parsing.Pseudo(): | 
					
						
							| 
									
										
										
										
											2023-06-14 10:06:50 -07:00
										 |  |  |                 instr = self.pseudo_instrs[thing.name] | 
					
						
							| 
									
										
										
										
											2023-09-20 18:58:23 +02:00
										 |  |  |                 # Calculate stack effect, and check that it's the same | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |                 # for all targets. | 
					
						
							| 
									
										
										
										
											2023-08-25 18:08:29 +01:00
										 |  |  |                 for target in self.pseudos[thing.name].targets: | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |                     target_instr = self.instrs.get(target) | 
					
						
							| 
									
										
										
										
											2023-11-01 10:53:27 +00:00
										 |  |  |                     if target_instr is None: | 
					
						
							|  |  |  |                         macro_instr = self.macro_instrs[target] | 
					
						
							|  |  |  |                         popped, pushed = stacking.get_stack_effect_info_for_macro(macro_instr) | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |                     else: | 
					
						
							| 
									
										
										
										
											2023-11-01 10:53:27 +00:00
										 |  |  |                         target_popped = effect_str(target_instr.input_effects) | 
					
						
							|  |  |  |                         target_pushed = effect_str(target_instr.output_effects) | 
					
						
							|  |  |  |                         if popped is None: | 
					
						
							|  |  |  |                             popped, pushed = target_popped, target_pushed | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             assert popped == target_popped | 
					
						
							|  |  |  |                             assert pushed == target_pushed | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |             case _: | 
					
						
							| 
									
										
										
										
											2023-08-21 00:40:41 +01:00
										 |  |  |                 assert_never(thing) | 
					
						
							| 
									
										
										
										
											2023-08-25 18:08:29 +01:00
										 |  |  |         assert popped is not None and pushed is not None | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |         return instr, popped, pushed | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |     @contextlib.contextmanager | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |     def metadata_item(self, signature: str, open: str, close: str) -> Iterator[None]: | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |         self.out.emit("") | 
					
						
							|  |  |  |         self.out.emit(f"extern {signature};") | 
					
						
							|  |  |  |         self.out.emit("#ifdef NEED_OPCODE_METADATA") | 
					
						
							|  |  |  |         with self.out.block(f"{signature} {open}", close): | 
					
						
							|  |  |  |             yield | 
					
						
							|  |  |  |         self.out.emit("#endif // NEED_OPCODE_METADATA") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |     def write_stack_effect_functions(self) -> None: | 
					
						
							| 
									
										
										
										
											2023-01-29 17:28:39 -08:00
										 |  |  |         popped_data: list[tuple[AnyInstruction, str]] = [] | 
					
						
							|  |  |  |         pushed_data: list[tuple[AnyInstruction, str]] = [] | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |         for thing in self.everything: | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |             if isinstance(thing, parsing.Macro) and thing.name in self.instrs: | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |             instr, popped, pushed = self.get_stack_effect_info(thing) | 
					
						
							| 
									
										
										
										
											2023-01-26 09:15:05 -08:00
										 |  |  |             if instr is not None: | 
					
						
							| 
									
										
										
										
											2023-01-29 17:28:39 -08:00
										 |  |  |                 popped_data.append((instr, popped)) | 
					
						
							|  |  |  |                 pushed_data.append((instr, pushed)) | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-30 17:27:51 -08:00
										 |  |  |         def write_function( | 
					
						
							|  |  |  |             direction: str, data: list[tuple[AnyInstruction, str]] | 
					
						
							|  |  |  |         ) -> None: | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |             with self.metadata_item( | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |                 f"int _PyOpcode_num_{direction}(int opcode, int oparg, bool jump)", | 
					
						
							|  |  |  |                 "", | 
					
						
							|  |  |  |                 "", | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |             ): | 
					
						
							|  |  |  |                 with self.out.block("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("    return -1;") | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-29 17:28:39 -08:00
										 |  |  |         write_function("popped", popped_data) | 
					
						
							|  |  |  |         write_function("pushed", pushed_data) | 
					
						
							| 
									
										
										
										
											2023-02-08 16:23:19 -08:00
										 |  |  |         self.out.emit("") | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-03 20:59:21 -08:00
										 |  |  |     def from_source_files(self) -> str: | 
					
						
							| 
									
										
										
										
											2023-07-20 16:08:52 -07:00
										 |  |  |         filenames = [] | 
					
						
							|  |  |  |         for filename in self.input_filenames: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 filename = os.path.relpath(filename, ROOT) | 
					
						
							|  |  |  |             except ValueError: | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 # May happen on Windows if root and temp on different volumes | 
					
						
							| 
									
										
										
										
											2023-07-20 16:08:52 -07:00
										 |  |  |                 pass | 
					
						
							| 
									
										
										
										
											2023-08-22 20:49:39 +01:00
										 |  |  |             filenames.append(filename.replace(os.path.sep, posixpath.sep)) | 
					
						
							| 
									
										
										
										
											2023-07-20 16:08:52 -07:00
										 |  |  |         paths = f"\n{self.out.comment}   ".join(filenames) | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  |         return f"{self.out.comment} from:\n{self.out.comment}   {paths}\n" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |     def write_provenance_header(self) -> None: | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  |         self.out.write_raw(f"{self.out.comment} This file is generated by {THIS}\n") | 
					
						
							|  |  |  |         self.out.write_raw(self.from_source_files()) | 
					
						
							|  |  |  |         self.out.write_raw(f"{self.out.comment} Do not edit!\n") | 
					
						
							| 
									
										
										
										
											2023-03-03 20:59:21 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |     def assign_opcode_ids(self) -> None: | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |         """Assign IDs to opcodes""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |         ops: list[tuple[bool, str]] = []  # (has_arg, name) for each opcode | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |         instrumented_ops: list[str] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |         specialized_ops: set[str] = set() | 
					
						
							| 
									
										
										
										
											2023-09-12 11:36:17 +01:00
										 |  |  |         for name, family in self.families.items(): | 
					
						
							|  |  |  |             specialized_ops.update(family.members) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |         for instr in self.macro_instrs.values(): | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             name = instr.name | 
					
						
							| 
									
										
										
										
											2023-09-12 11:36:17 +01:00
										 |  |  |             if name in specialized_ops: | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |             if name.startswith("INSTRUMENTED_"): | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |                 instrumented_ops.append(name) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 ops.append((instr.instr_flags.HAS_ARG_FLAG, name)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Special case: this instruction is implemented in ceval.c | 
					
						
							|  |  |  |         # rather than bytecodes.c, so we need to add it explicitly | 
					
						
							|  |  |  |         # here (at least until we add something to bytecodes.c to | 
					
						
							|  |  |  |         # declare external instructions). | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |         instrumented_ops.append("INSTRUMENTED_LINE") | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # assert lists are unique | 
					
						
							|  |  |  |         assert len(set(ops)) == len(ops) | 
					
						
							|  |  |  |         assert len(set(instrumented_ops)) == len(instrumented_ops) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |         opname: list[str | None] = [None] * 512 | 
					
						
							|  |  |  |         opmap: dict[str, int] = {} | 
					
						
							|  |  |  |         markers: dict[str, int] = {} | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |         def map_op(op: int, name: str) -> None: | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             assert op < len(opname) | 
					
						
							| 
									
										
										
										
											2023-09-12 11:36:17 +01:00
										 |  |  |             assert opname[op] is None, (op, name) | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             assert name not in opmap | 
					
						
							|  |  |  |             opname[op] = name | 
					
						
							|  |  |  |             opmap[name] = op | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # 0 is reserved for cache entries. This helps debugging. | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |         map_op(0, "CACHE") | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # 17 is reserved as it is the initial value for the specializing counter. | 
					
						
							|  |  |  |         # This helps catch cases where we attempt to execute a cache. | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |         map_op(17, "RESERVED") | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-12 11:36:17 +01:00
										 |  |  |         # 149 is RESUME - it is hard coded as such in Tools/build/deepfreeze.py | 
					
						
							|  |  |  |         map_op(149, "RESUME") | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-12 11:36:17 +01:00
										 |  |  |         # Specialized ops appear in their own section | 
					
						
							|  |  |  |         # Instrumented opcodes are at the end of the valid range | 
					
						
							|  |  |  |         min_internal = 150 | 
					
						
							|  |  |  |         min_instrumented = 254 - (len(instrumented_ops) - 1) | 
					
						
							|  |  |  |         assert min_internal + len(specialized_ops) < min_instrumented | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-12 11:36:17 +01:00
										 |  |  |         next_opcode = 1 | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |         for has_arg, name in sorted(ops): | 
					
						
							|  |  |  |             if name in opmap: | 
					
						
							|  |  |  |                 continue  # an anchored name, like CACHE | 
					
						
							|  |  |  |             map_op(next_opcode, name) | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |             if has_arg and "HAVE_ARGUMENT" not in markers: | 
					
						
							|  |  |  |                 markers["HAVE_ARGUMENT"] = next_opcode | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-12 11:36:17 +01:00
										 |  |  |             while opname[next_opcode] is not None: | 
					
						
							|  |  |  |                 next_opcode += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |         assert next_opcode < min_internal, next_opcode | 
					
						
							| 
									
										
										
										
											2023-09-12 11:36:17 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for i, op in enumerate(sorted(specialized_ops)): | 
					
						
							|  |  |  |             map_op(min_internal + i, op) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |         markers["MIN_INSTRUMENTED_OPCODE"] = min_instrumented | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |         for i, op in enumerate(instrumented_ops): | 
					
						
							|  |  |  |             map_op(min_instrumented + i, op) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Pseudo opcodes are after the valid range | 
					
						
							|  |  |  |         for i, op in enumerate(sorted(self.pseudos)): | 
					
						
							|  |  |  |             map_op(256 + i, op) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |         assert 255 not in opmap.values()  # 255 is reserved | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |         self.opmap = opmap | 
					
						
							|  |  |  |         self.markers = markers | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |     def write_opcode_ids( | 
					
						
							|  |  |  |         self, opcode_ids_h_filename: str, opcode_targets_filename: str | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |         """Write header file that defined the opcode IDs""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with open(opcode_ids_h_filename, "w") as f: | 
					
						
							|  |  |  |             # Create formatter | 
					
						
							|  |  |  |             self.out = Formatter(f, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.write_provenance_header() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit("#ifndef Py_OPCODE_IDS_H") | 
					
						
							|  |  |  |             self.out.emit("#define Py_OPCODE_IDS_H") | 
					
						
							|  |  |  |             self.out.emit("#ifdef __cplusplus") | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |             self.out.emit('extern "C" {') | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             self.out.emit("#endif") | 
					
						
							|  |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit("/* Instruction opcodes for compiled code */") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |             def define(name: str, opcode: int) -> None: | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |                 self.out.emit(f"#define {name:<38} {opcode:>3}") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |             all_pairs: list[tuple[int, int, str]] = [] | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             # the second item in the tuple sorts the markers before the ops | 
					
						
							|  |  |  |             all_pairs.extend((i, 1, name) for (name, i) in self.markers.items()) | 
					
						
							|  |  |  |             all_pairs.extend((i, 2, name) for (name, i) in self.opmap.items()) | 
					
						
							|  |  |  |             for i, _, name in sorted(all_pairs): | 
					
						
							|  |  |  |                 assert name is not None | 
					
						
							|  |  |  |                 define(name, i) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit("#ifdef __cplusplus") | 
					
						
							|  |  |  |             self.out.emit("}") | 
					
						
							|  |  |  |             self.out.emit("#endif") | 
					
						
							|  |  |  |             self.out.emit("#endif /* !Py_OPCODE_IDS_H */") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with open(opcode_targets_filename, "w") as f: | 
					
						
							|  |  |  |             # Create formatter | 
					
						
							|  |  |  |             self.out = Formatter(f, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             with self.out.block("static void *opcode_targets[256] =", ";"): | 
					
						
							|  |  |  |                 targets = ["_unknown_opcode"] * 256 | 
					
						
							|  |  |  |                 for name, op in self.opmap.items(): | 
					
						
							|  |  |  |                     if op < 256: | 
					
						
							|  |  |  |                         targets[op] = f"TARGET_{name}" | 
					
						
							|  |  |  |                 f.write(",\n".join([f"    &&{s}" for s in targets])) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     def write_metadata(self, metadata_filename: str, pymetadata_filename: str) -> None: | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  |         """Write instruction metadata to output file.""" | 
					
						
							| 
									
										
										
										
											2023-01-09 15:53:01 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Compute the set of all instruction formats. | 
					
						
							|  |  |  |         all_formats: set[str] = set() | 
					
						
							|  |  |  |         for thing in self.everything: | 
					
						
							| 
									
										
										
										
											2023-08-25 18:08:29 +01:00
										 |  |  |             format: str | None = None | 
					
						
							| 
									
										
										
										
											2023-01-09 15:53:01 -08:00
										 |  |  |             match thing: | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 case parsing.InstDef(): | 
					
						
							| 
									
										
										
										
											2023-01-09 15:53:01 -08:00
										 |  |  |                     format = self.instrs[thing.name].instr_fmt | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 case parsing.Macro(): | 
					
						
							| 
									
										
										
										
											2023-01-09 15:53:01 -08:00
										 |  |  |                     format = self.macro_instrs[thing.name].instr_fmt | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 case parsing.Pseudo(): | 
					
						
							| 
									
										
										
										
											2023-10-31 13:21:07 +00:00
										 |  |  |                     # Pseudo instructions exist only in the compiler, | 
					
						
							|  |  |  |                     # so do not have a format | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2023-01-09 15:53:01 -08:00
										 |  |  |                 case _: | 
					
						
							| 
									
										
										
										
											2023-08-21 00:40:41 +01:00
										 |  |  |                     assert_never(thing) | 
					
						
							| 
									
										
										
										
											2023-08-25 18:08:29 +01:00
										 |  |  |             assert format is not None | 
					
						
							| 
									
										
										
										
											2023-01-09 15:53:01 -08:00
										 |  |  |             all_formats.add(format) | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Turn it into a sorted list of enum values. | 
					
						
							| 
									
										
										
										
											2023-01-09 15:53:01 -08:00
										 |  |  |         format_enums = [INSTR_FMT_PREFIX + format for format in sorted(all_formats)] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |         with open(metadata_filename, "w") as f: | 
					
						
							| 
									
										
										
										
											2023-03-15 08:37:36 -07:00
										 |  |  |             # Create formatter | 
					
						
							|  |  |  |             self.out = Formatter(f, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  |             self.write_provenance_header() | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit("#ifndef Py_BUILD_CORE") | 
					
						
							|  |  |  |             self.out.emit('#  error "this header requires Py_BUILD_CORE define"') | 
					
						
							|  |  |  |             self.out.emit("#endif") | 
					
						
							|  |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit("#include <stdbool.h>              // bool") | 
					
						
							| 
									
										
										
										
											2023-07-14 18:41:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |             self.write_pseudo_instrs() | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.write_uop_items(lambda name, counter: f"#define {name} {counter}") | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-25 20:41:03 +00:00
										 |  |  |             self.write_stack_effect_functions() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  |             # Write the enum definition for instruction formats. | 
					
						
							|  |  |  |             with self.out.block("enum InstructionFormat", ";"): | 
					
						
							|  |  |  |                 for enum in format_enums: | 
					
						
							|  |  |  |                     self.out.emit(enum + ",") | 
					
						
							| 
									
										
										
										
											2023-06-22 00:14:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-14 18:41:52 +01:00
										 |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit( | 
					
						
							|  |  |  |                 "#define IS_VALID_OPCODE(OP) \\\n" | 
					
						
							|  |  |  |                 "    (((OP) >= 0) && ((OP) < OPCODE_METADATA_SIZE) && \\\n" | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 "     (_PyOpcode_opcode_metadata[(OP)].valid_entry))" | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2023-07-14 18:41:52 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             self.out.emit("") | 
					
						
							| 
									
										
										
										
											2023-06-22 00:14:43 +01:00
										 |  |  |             InstructionFlags.emit_macros(self.out) | 
					
						
							| 
									
										
										
										
											2023-06-13 21:42:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-14 18:41:52 +01:00
										 |  |  |             self.out.emit("") | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             with self.out.block("struct opcode_metadata", ";"): | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  |                 self.out.emit("bool valid_entry;") | 
					
						
							| 
									
										
										
										
											2023-01-09 15:53:01 -08:00
										 |  |  |                 self.out.emit("enum InstructionFormat instr_format;") | 
					
						
							| 
									
										
										
										
											2023-06-13 21:42:03 +01:00
										 |  |  |                 self.out.emit("int flags;") | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             self.out.emit("") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             with self.out.block("struct opcode_macro_expansion", ";"): | 
					
						
							|  |  |  |                 self.out.emit("int nuops;") | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 self.out.emit( | 
					
						
							| 
									
										
										
										
											2023-08-24 17:36:00 -07:00
										 |  |  |                     "struct { int16_t uop; int8_t size; int8_t offset; } uops[12];" | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             self.out.emit("") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-07 10:42:10 -07:00
										 |  |  |             for key, value in OPARG_SIZES.items(): | 
					
						
							|  |  |  |                 self.out.emit(f"#define {key} {value}") | 
					
						
							| 
									
										
										
										
											2023-02-08 16:23:19 -08:00
										 |  |  |             self.out.emit("") | 
					
						
							| 
									
										
										
										
											2023-07-07 10:42:10 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             self.out.emit( | 
					
						
							| 
									
										
										
										
											2023-10-31 13:21:07 +00:00
										 |  |  |                 "#define OPCODE_METADATA_FLAGS(OP) " | 
					
						
							|  |  |  |                 "(_PyOpcode_opcode_metadata[(OP)].flags & (HAS_ARG_FLAG | HAS_JUMP_FLAG))" | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |             self.out.emit("#define SAME_OPCODE_METADATA(OP1, OP2) \\") | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             self.out.emit( | 
					
						
							| 
									
										
										
										
											2023-10-31 13:21:07 +00:00
										 |  |  |                 "        (OPCODE_METADATA_FLAGS(OP1) == OPCODE_METADATA_FLAGS(OP2))" | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |             self.out.emit("") | 
					
						
							| 
									
										
										
										
											2023-02-08 16:23:19 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Write metadata array declaration | 
					
						
							| 
									
										
										
										
											2023-07-14 18:41:52 +01:00
										 |  |  |             self.out.emit("#define OPCODE_METADATA_SIZE 512") | 
					
						
							|  |  |  |             self.out.emit("#define OPCODE_UOP_NAME_SIZE 512") | 
					
						
							|  |  |  |             self.out.emit("#define OPCODE_MACRO_EXPANSION_SIZE 256") | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |             with self.metadata_item( | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 "const struct opcode_metadata " | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |                 "_PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE]", | 
					
						
							|  |  |  |                 "=", | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |                 ";", | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |             ): | 
					
						
							|  |  |  |                 # Write metadata for each instruction | 
					
						
							|  |  |  |                 for thing in self.everything: | 
					
						
							|  |  |  |                     match thing: | 
					
						
							|  |  |  |                         case parsing.InstDef(): | 
					
						
							| 
									
										
										
										
											2023-08-29 09:51:51 -07:00
										 |  |  |                             self.write_metadata_for_inst(self.instrs[thing.name]) | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |                         case parsing.Macro(): | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |                             if thing.name not in self.instrs: | 
					
						
							|  |  |  |                                 self.write_metadata_for_macro( | 
					
						
							|  |  |  |                                     self.macro_instrs[thing.name] | 
					
						
							|  |  |  |                                 ) | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |                         case parsing.Pseudo(): | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |                             self.write_metadata_for_pseudo( | 
					
						
							|  |  |  |                                 self.pseudo_instrs[thing.name] | 
					
						
							|  |  |  |                             ) | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |                         case _: | 
					
						
							| 
									
										
										
										
											2023-08-21 00:40:41 +01:00
										 |  |  |                             assert_never(thing) | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |             with self.metadata_item( | 
					
						
							| 
									
										
										
										
											2023-07-14 18:41:52 +01:00
										 |  |  |                 "const struct opcode_macro_expansion " | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |                 "_PyOpcode_macro_expansion[OPCODE_MACRO_EXPANSION_SIZE]", | 
					
						
							|  |  |  |                 "=", | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |                 ";", | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             ): | 
					
						
							|  |  |  |                 # Write macro expansion for each non-pseudo instruction | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |                 for mac in self.macro_instrs.values(): | 
					
						
							|  |  |  |                     if is_super_instruction(mac): | 
					
						
							|  |  |  |                         # Special-case the heck out of super-instructions | 
					
						
							|  |  |  |                         self.write_super_expansions(mac.name) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         self.write_macro_expansions( | 
					
						
							|  |  |  |                             mac.name, mac.parts, mac.cache_offset | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-14 19:36:29 +01:00
										 |  |  |             with self.metadata_item( | 
					
						
							|  |  |  |                 "const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE]", "=", ";" | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             ): | 
					
						
							|  |  |  |                 self.write_uop_items(lambda name, counter: f'[{name}] = "{name}",') | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             with self.metadata_item( | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |                 f"const char *const _PyOpcode_OpName[{1 + max(self.opmap.values())}]", | 
					
						
							|  |  |  |                 "=", | 
					
						
							|  |  |  |                 ";", | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             ): | 
					
						
							|  |  |  |                 for name in self.opmap: | 
					
						
							|  |  |  |                     self.out.emit(f'[{name}] = "{name}",') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-23 18:39:00 +01:00
										 |  |  |             with self.metadata_item( | 
					
						
							|  |  |  |                 f"const uint8_t _PyOpcode_Caches[256]", | 
					
						
							|  |  |  |                 "=", | 
					
						
							|  |  |  |                 ";", | 
					
						
							|  |  |  |             ): | 
					
						
							| 
									
										
										
										
											2023-09-11 11:20:24 -07:00
										 |  |  |                 family_member_names: set[str] = set() | 
					
						
							|  |  |  |                 for family in self.families.values(): | 
					
						
							|  |  |  |                     family_member_names.update(family.members) | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |                 for mac in self.macro_instrs.values(): | 
					
						
							| 
									
										
										
										
											2023-09-11 11:20:24 -07:00
										 |  |  |                     if ( | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |                         mac.cache_offset > 0 | 
					
						
							|  |  |  |                         and mac.name not in family_member_names | 
					
						
							|  |  |  |                         and not mac.name.startswith("INSTRUMENTED_") | 
					
						
							| 
									
										
										
										
											2023-09-11 11:20:24 -07:00
										 |  |  |                     ): | 
					
						
							|  |  |  |                         self.out.emit(f"[{mac.name}] = {mac.cache_offset},") | 
					
						
							| 
									
										
										
										
											2023-08-23 18:39:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             deoptcodes = {} | 
					
						
							|  |  |  |             for name, op in self.opmap.items(): | 
					
						
							|  |  |  |                 if op < 256: | 
					
						
							|  |  |  |                     deoptcodes[name] = name | 
					
						
							|  |  |  |             for name, family in self.families.items(): | 
					
						
							|  |  |  |                 for m in family.members: | 
					
						
							|  |  |  |                     deoptcodes[m] = name | 
					
						
							|  |  |  |             # special case: | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |             deoptcodes["BINARY_OP_INPLACE_ADD_UNICODE"] = "BINARY_OP" | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |             with self.metadata_item(f"const uint8_t _PyOpcode_Deopt[256]", "=", ";"): | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |                 for opt, deopt in sorted(deoptcodes.items()): | 
					
						
							|  |  |  |                     self.out.emit(f"[{opt}] = {deopt},") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit("#define EXTRA_CASES \\") | 
					
						
							|  |  |  |             valid_opcodes = set(self.opmap.values()) | 
					
						
							|  |  |  |             with self.out.indent(): | 
					
						
							|  |  |  |                 for op in range(256): | 
					
						
							|  |  |  |                     if op not in valid_opcodes: | 
					
						
							|  |  |  |                         self.out.emit(f"case {op}: \\") | 
					
						
							|  |  |  |                 self.out.emit("    ;\n") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |         with open(pymetadata_filename, "w") as f: | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  |             # Create formatter | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             self.out = Formatter(f, 0, comment="#") | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             self.write_provenance_header() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             # emit specializations | 
					
						
							|  |  |  |             specialized_ops = set() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit("_specializations = {") | 
					
						
							|  |  |  |             for name, family in self.families.items(): | 
					
						
							|  |  |  |                 with self.out.indent(): | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                     self.out.emit(f'"{family.name}": [') | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  |                     with self.out.indent(): | 
					
						
							| 
									
										
										
										
											2023-07-16 11:16:34 -04:00
										 |  |  |                         for m in family.members: | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                             self.out.emit(f'"{m}",') | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |                         specialized_ops.update(family.members) | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  |                     self.out.emit(f"],") | 
					
						
							|  |  |  |             self.out.emit("}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Handle special case | 
					
						
							|  |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             self.out.emit("# An irregular case:") | 
					
						
							|  |  |  |             self.out.emit( | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                 '_specializations["BINARY_OP"].append(' | 
					
						
							|  |  |  |                 '"BINARY_OP_INPLACE_ADD_UNICODE")' | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             specialized_ops.add("BINARY_OP_INPLACE_ADD_UNICODE") | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             ops = sorted((id, name) for (name, id) in self.opmap.items()) | 
					
						
							|  |  |  |             # emit specialized opmap | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  |             self.out.emit("") | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |             with self.out.block("_specialized_opmap ="): | 
					
						
							|  |  |  |                 for op, name in ops: | 
					
						
							|  |  |  |                     if name in specialized_ops: | 
					
						
							|  |  |  |                         self.out.emit(f"'{name}': {op},") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # emit opmap | 
					
						
							|  |  |  |             self.out.emit("") | 
					
						
							|  |  |  |             with self.out.block("opmap ="): | 
					
						
							|  |  |  |                 for op, name in ops: | 
					
						
							|  |  |  |                     if name not in specialized_ops: | 
					
						
							|  |  |  |                         self.out.emit(f"'{name}': {op},") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |             for name in ["MIN_INSTRUMENTED_OPCODE", "HAVE_ARGUMENT"]: | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  |                 self.out.emit(f"{name} = {self.markers[name]}") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |     def write_pseudo_instrs(self) -> None: | 
					
						
							|  |  |  |         """Write the IS_PSEUDO_INSTR macro""" | 
					
						
							| 
									
										
										
										
											2023-07-01 11:28:07 +01:00
										 |  |  |         self.out.emit("\n\n#define IS_PSEUDO_INSTR(OP)  ( \\") | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |         for op in self.pseudos: | 
					
						
							|  |  |  |             self.out.emit(f"    ((OP) == {op}) || \\") | 
					
						
							| 
									
										
										
										
											2023-07-01 11:28:07 +01:00
										 |  |  |         self.out.emit(f"    0)") | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |     def write_uop_items(self, make_text: typing.Callable[[str, int], str]) -> None: | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |         """Write '#define XXX NNN' for each uop""" | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |         counter = 300  # TODO: Avoid collision with pseudo instructions | 
					
						
							| 
									
										
										
										
											2023-07-13 12:14:51 -07:00
										 |  |  |         seen = set() | 
					
						
							| 
									
										
										
										
											2023-07-10 16:04:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |         def add(name: str) -> None: | 
					
						
							| 
									
										
										
										
											2023-07-13 12:14:51 -07:00
										 |  |  |             if name in seen: | 
					
						
							|  |  |  |                 return | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             nonlocal counter | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |             self.out.emit(make_text(name, counter)) | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             counter += 1 | 
					
						
							| 
									
										
										
										
											2023-07-13 12:14:51 -07:00
										 |  |  |             seen.add(name) | 
					
						
							| 
									
										
										
										
											2023-07-10 16:04:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-13 12:14:51 -07:00
										 |  |  |         # These two are first by convention | 
					
						
							| 
									
										
										
										
											2023-09-11 15:39:19 -07:00
										 |  |  |         add("_EXIT_TRACE") | 
					
						
							|  |  |  |         add("_SET_IP") | 
					
						
							| 
									
										
										
										
											2023-07-10 16:04:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |         for instr in self.instrs.values(): | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |             # Skip ops that are also macros -- those are desugared inst()s | 
					
						
							|  |  |  |             if instr.name not in self.macros: | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |                 add(instr.name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 16:26:43 -07:00
										 |  |  |     def write_macro_expansions( | 
					
						
							|  |  |  |         self, name: str, parts: MacroParts, cache_offset: int | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |         """Write the macro expansions for a macro-instruction.""" | 
					
						
							|  |  |  |         # TODO: Refactor to share code with write_cody(), is_viaible_uop(), etc. | 
					
						
							|  |  |  |         offset = 0  # Cache effect offset | 
					
						
							|  |  |  |         expansions: list[tuple[str, int, int]] = []  # [(name, size, offset), ...] | 
					
						
							| 
									
										
										
										
											2023-06-29 13:02:25 -07:00
										 |  |  |         for part in parts: | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |             if isinstance(part, Component): | 
					
						
							| 
									
										
										
										
											2023-11-07 09:42:39 +00:00
										 |  |  |                 # Skip specializations | 
					
						
							|  |  |  |                 if "specializing" in part.instr.annotations: | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 # All other component instructions must be viable uops | 
					
						
							| 
									
										
										
										
											2023-10-26 14:43:10 +01:00
										 |  |  |                 if not part.instr.is_viable_uop(): | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  |                     # This note just reminds us about macros that cannot | 
					
						
							|  |  |  |                     # be expanded to Tier 2 uops. It is not an error. | 
					
						
							|  |  |  |                     # It is sometimes emitted for macros that have a | 
					
						
							|  |  |  |                     # manual translation in translate_bytecode_to_trace() | 
					
						
							|  |  |  |                     # in Python/optimizer.c. | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |                     if len(parts) > 1 or part.instr.name != name: | 
					
						
							|  |  |  |                         self.note( | 
					
						
							|  |  |  |                             f"Part {part.instr.name} of {name} is not a viable uop", | 
					
						
							|  |  |  |                             part.instr.inst, | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |                     return | 
					
						
							| 
									
										
										
										
											2023-07-17 12:12:33 -07:00
										 |  |  |                 if not part.active_caches: | 
					
						
							| 
									
										
										
										
											2023-10-26 14:43:10 +01:00
										 |  |  |                     if part.instr.name == "_SAVE_RETURN_OFFSET": | 
					
						
							|  |  |  |                         size, offset = OPARG_SIZES["OPARG_SAVE_RETURN_OFFSET"], cache_offset | 
					
						
							| 
									
										
										
										
											2023-08-16 16:26:43 -07:00
										 |  |  |                     else: | 
					
						
							|  |  |  |                         size, offset = OPARG_SIZES["OPARG_FULL"], 0 | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     # If this assert triggers, is_viable_uops() lied | 
					
						
							| 
									
										
										
										
											2023-06-29 13:02:25 -07:00
										 |  |  |                     assert len(part.active_caches) == 1, (name, part.instr.name) | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |                     cache = part.active_caches[0] | 
					
						
							|  |  |  |                     size, offset = cache.effect.size, cache.offset | 
					
						
							|  |  |  |                 expansions.append((part.instr.name, size, offset)) | 
					
						
							| 
									
										
										
										
											2023-06-29 13:02:25 -07:00
										 |  |  |         assert len(expansions) > 0, f"Macro {name} has empty expansion?!" | 
					
						
							| 
									
										
										
										
											2023-07-07 10:42:10 -07:00
										 |  |  |         self.write_expansions(name, expansions) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def write_super_expansions(self, name: str) -> None: | 
					
						
							|  |  |  |         """Write special macro expansions for super-instructions.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If you get an assertion failure here, you probably have accidentally | 
					
						
							|  |  |  |         violated one of the assumptions here. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         - A super-instruction's name is of the form FIRST_SECOND where | 
					
						
							|  |  |  |           FIRST and SECOND are regular instructions whose name has the | 
					
						
							|  |  |  |           form FOO_BAR. Thus, there must be exactly 3 underscores. | 
					
						
							|  |  |  |           Example: LOAD_CONST_STORE_FAST. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         - A super-instruction's body uses `oparg1 and `oparg2`, and no | 
					
						
							|  |  |  |           other instruction's body uses those variable names. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         - A super-instruction has no active (used) cache entries. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         In the expansion, the first instruction's operand is all but the | 
					
						
							|  |  |  |         bottom 4 bits of the super-instruction's oparg, and the second | 
					
						
							|  |  |  |         instruction's operand is the bottom 4 bits. We use the special | 
					
						
							|  |  |  |         size codes OPARG_TOP and OPARG_BOTTOM for these. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         pieces = name.split("_") | 
					
						
							|  |  |  |         assert len(pieces) == 4, f"{name} doesn't look like a super-instr" | 
					
						
							|  |  |  |         name1 = "_".join(pieces[:2]) | 
					
						
							|  |  |  |         name2 = "_".join(pieces[2:]) | 
					
						
							|  |  |  |         assert name1 in self.instrs, f"{name1} doesn't match any instr" | 
					
						
							|  |  |  |         assert name2 in self.instrs, f"{name2} doesn't match any instr" | 
					
						
							|  |  |  |         instr1 = self.instrs[name1] | 
					
						
							|  |  |  |         instr2 = self.instrs[name2] | 
					
						
							|  |  |  |         assert not instr1.active_caches, f"{name1} has active caches" | 
					
						
							|  |  |  |         assert not instr2.active_caches, f"{name2} has active caches" | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  |         expansions: list[tuple[str, int, int]] = [ | 
					
						
							| 
									
										
										
										
											2023-07-07 10:42:10 -07:00
										 |  |  |             (name1, OPARG_SIZES["OPARG_TOP"], 0), | 
					
						
							|  |  |  |             (name2, OPARG_SIZES["OPARG_BOTTOM"], 0), | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         self.write_expansions(name, expansions) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     def write_expansions( | 
					
						
							|  |  |  |         self, name: str, expansions: list[tuple[str, int, int]] | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							|  |  |  |         pieces = [ | 
					
						
							|  |  |  |             f"{{ {name}, {size}, {offset} }}" for name, size, offset in expansions | 
					
						
							|  |  |  |         ] | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |         self.out.emit( | 
					
						
							| 
									
										
										
										
											2023-06-29 13:02:25 -07:00
										 |  |  |             f"[{name}] = " | 
					
						
							| 
									
										
										
										
											2023-07-07 10:42:10 -07:00
										 |  |  |             f"{{ .nuops = {len(pieces)}, .uops = {{ {', '.join(pieces)} }} }}," | 
					
						
							| 
									
										
										
										
											2023-06-28 11:28:07 -07:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-31 13:21:07 +00:00
										 |  |  |     def emit_metadata_entry(self, name: str, fmt: str | None, flags: InstructionFlags) -> None: | 
					
						
							| 
									
										
										
										
											2023-06-22 00:14:43 +01:00
										 |  |  |         flag_names = flags.names(value=True) | 
					
						
							|  |  |  |         if not flag_names: | 
					
						
							|  |  |  |             flag_names.append("0") | 
					
						
							| 
									
										
										
										
											2023-10-31 13:21:07 +00:00
										 |  |  |         fmt_macro = "0" if fmt is None else INSTR_FMT_PREFIX + fmt | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  |         self.out.emit( | 
					
						
							| 
									
										
										
										
											2023-10-31 13:21:07 +00:00
										 |  |  |             f"[{name}] = {{ true, {fmt_macro}," | 
					
						
							| 
									
										
										
										
											2023-06-22 00:14:43 +01:00
										 |  |  |             f" {' | '.join(flag_names)} }}," | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-13 21:42:03 +01:00
										 |  |  |     def write_metadata_for_inst(self, instr: Instruction) -> None: | 
					
						
							|  |  |  |         """Write metadata for a single instruction.""" | 
					
						
							| 
									
										
										
										
											2023-06-22 00:14:43 +01:00
										 |  |  |         self.emit_metadata_entry(instr.name, instr.instr_fmt, instr.instr_flags) | 
					
						
							| 
									
										
										
										
											2023-06-13 21:42:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  |     def write_metadata_for_macro(self, mac: MacroInstruction) -> None: | 
					
						
							|  |  |  |         """Write metadata for a macro-instruction.""" | 
					
						
							| 
									
										
										
										
											2023-06-22 00:14:43 +01:00
										 |  |  |         self.emit_metadata_entry(mac.name, mac.instr_fmt, mac.instr_flags) | 
					
						
							| 
									
										
										
										
											2023-01-05 13:01:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  |     def write_metadata_for_pseudo(self, ps: PseudoInstruction) -> None: | 
					
						
							|  |  |  |         """Write metadata for a macro-instruction.""" | 
					
						
							| 
									
										
										
										
											2023-10-31 13:21:07 +00:00
										 |  |  |         self.emit_metadata_entry(ps.name, None, ps.instr_flags) | 
					
						
							| 
									
										
										
										
											2023-06-11 22:31:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     def write_instructions( | 
					
						
							|  |  |  |         self, output_filename: str, emit_line_directives: bool | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							| 
									
										
										
										
											2022-11-17 17:06:07 -08:00
										 |  |  |         """Write instructions to output file.""" | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |         with open(output_filename, "w") as f: | 
					
						
							| 
									
										
										
										
											2023-03-15 08:37:36 -07:00
										 |  |  |             # Create formatter | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |             self.out = Formatter(f, 8, emit_line_directives) | 
					
						
							| 
									
										
										
										
											2022-11-17 17:06:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             self.write_provenance_header() | 
					
						
							| 
									
										
										
										
											2022-12-02 19:57:30 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 13:13:02 -07:00
										 |  |  |             self.out.write_raw("\n") | 
					
						
							|  |  |  |             self.out.write_raw("#ifdef TIER_TWO\n") | 
					
						
							|  |  |  |             self.out.write_raw("    #error \"This file is for Tier 1 only\"\n") | 
					
						
							|  |  |  |             self.out.write_raw("#endif\n") | 
					
						
							|  |  |  |             self.out.write_raw("#define TIER_ONE 1\n") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-08 15:54:07 -08:00
										 |  |  |             # Write and count instructions of all kinds | 
					
						
							| 
									
										
										
										
											2022-12-02 19:57:30 -08:00
										 |  |  |             n_macros = 0 | 
					
						
							| 
									
										
										
										
											2022-12-08 15:54:07 -08:00
										 |  |  |             for thing in self.everything: | 
					
						
							|  |  |  |                 match thing: | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                     case parsing.InstDef(): | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |                         pass | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                     case parsing.Macro(): | 
					
						
							| 
									
										
										
										
											2022-12-08 15:54:07 -08:00
										 |  |  |                         n_macros += 1 | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  |                         mac = self.macro_instrs[thing.name] | 
					
						
							| 
									
										
										
										
											2023-10-03 10:13:50 -07:00
										 |  |  |                         stacking.write_macro_instr(mac, self.out) | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |                     case parsing.Pseudo(): | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  |                         pass | 
					
						
							| 
									
										
										
										
											2022-12-08 15:54:07 -08:00
										 |  |  |                     case _: | 
					
						
							| 
									
										
										
										
											2023-08-21 00:40:41 +01:00
										 |  |  |                         assert_never(thing) | 
					
						
							| 
									
										
										
										
											2022-11-22 16:04:57 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 13:13:02 -07:00
										 |  |  |             self.out.write_raw("\n") | 
					
						
							|  |  |  |             self.out.write_raw("#undef TIER_ONE\n") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-02 19:57:30 -08:00
										 |  |  |         print( | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |             f"Wrote {n_macros} cases to {output_filename}", | 
					
						
							| 
									
										
										
										
											2022-12-02 19:57:30 -08:00
										 |  |  |             file=sys.stderr, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-11-22 16:04:57 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     def write_executor_instructions( | 
					
						
							|  |  |  |         self, executor_filename: str, emit_line_directives: bool | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |         """Generate cases for the Tier 2 interpreter.""" | 
					
						
							| 
									
										
										
										
											2023-08-04 09:35:56 -07:00
										 |  |  |         n_uops = 0 | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |         with open(executor_filename, "w") as f: | 
					
						
							|  |  |  |             self.out = Formatter(f, 8, emit_line_directives) | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             self.write_provenance_header() | 
					
						
							| 
									
										
										
										
											2023-11-01 13:13:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             self.out.write_raw("\n") | 
					
						
							|  |  |  |             self.out.write_raw("#ifdef TIER_ONE\n") | 
					
						
							|  |  |  |             self.out.write_raw("    #error \"This file is for Tier 2 only\"\n") | 
					
						
							|  |  |  |             self.out.write_raw("#endif\n") | 
					
						
							|  |  |  |             self.out.write_raw("#define TIER_TWO 2\n") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |             for instr in self.instrs.values(): | 
					
						
							|  |  |  |                 if instr.is_viable_uop(): | 
					
						
							|  |  |  |                     n_uops += 1 | 
					
						
							|  |  |  |                     self.out.emit("") | 
					
						
							|  |  |  |                     with self.out.block(f"case {instr.name}:"): | 
					
						
							|  |  |  |                         stacking.write_single_instr(instr, self.out, tier=TIER_TWO) | 
					
						
							|  |  |  |                         if instr.check_eval_breaker: | 
					
						
							|  |  |  |                             self.out.emit("CHECK_EVAL_BREAKER();") | 
					
						
							|  |  |  |                         self.out.emit("break;") | 
					
						
							| 
									
										
										
										
											2023-11-01 13:13:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             self.out.write_raw("\n") | 
					
						
							|  |  |  |             self.out.write_raw("#undef TIER_TWO\n") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |         print( | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |             f"Wrote {n_uops} cases to {executor_filename}", | 
					
						
							| 
									
										
										
										
											2023-06-26 19:02:57 -07:00
										 |  |  |             file=sys.stderr, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 02:04:17 +08:00
										 |  |  |     def write_abstract_interpreter_instructions( | 
					
						
							|  |  |  |         self, abstract_interpreter_filename: str, emit_line_directives: bool | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							|  |  |  |         """Generate cases for the Tier 2 abstract interpreter/analzyer.""" | 
					
						
							|  |  |  |         with open(abstract_interpreter_filename, "w") as f: | 
					
						
							|  |  |  |             self.out = Formatter(f, 8, emit_line_directives) | 
					
						
							|  |  |  |             self.write_provenance_header() | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  |             for instr in self.instrs.values(): | 
					
						
							|  |  |  |                 instr = AbstractInstruction(instr.inst) | 
					
						
							|  |  |  |                 if ( | 
					
						
							|  |  |  |                     instr.is_viable_uop() | 
					
						
							|  |  |  |                     and instr.name not in SPECIALLY_HANDLED_ABSTRACT_INSTR | 
					
						
							|  |  |  |                 ): | 
					
						
							|  |  |  |                     self.out.emit("") | 
					
						
							|  |  |  |                     with self.out.block(f"case {instr.name}:"): | 
					
						
							|  |  |  |                         instr.write(self.out, tier=TIER_TWO) | 
					
						
							|  |  |  |                         self.out.emit("break;") | 
					
						
							| 
									
										
										
										
											2023-08-16 02:04:17 +08:00
										 |  |  |         print( | 
					
						
							|  |  |  |             f"Wrote some stuff to {abstract_interpreter_filename}", | 
					
						
							|  |  |  |             file=sys.stderr, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-15 08:39:05 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | def is_super_instruction(mac: MacroInstruction) -> bool: | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |         len(mac.parts) == 1 | 
					
						
							|  |  |  |         and isinstance(mac.parts[0], Component) | 
					
						
							|  |  |  |         and variable_used(mac.parts[0].instr.inst, "oparg1") | 
					
						
							|  |  |  |     ): | 
					
						
							|  |  |  |         assert variable_used(mac.parts[0].instr.inst, "oparg2") | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2022-12-08 15:54:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-02 21:31:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  | def main() -> None: | 
					
						
							| 
									
										
										
										
											2022-11-17 17:06:07 -08:00
										 |  |  |     """Parse command line, parse input, analyze, write output.""" | 
					
						
							|  |  |  |     args = arg_parser.parse_args()  # Prints message and sys.exit(2) on error | 
					
						
							| 
									
										
										
										
											2023-03-03 20:59:21 -08:00
										 |  |  |     if len(args.input) == 0: | 
					
						
							|  |  |  |         args.input.append(DEFAULT_INPUT) | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Raises OSError if input unreadable | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     a = Generator(args.input) | 
					
						
							| 
									
										
										
										
											2023-06-19 23:47:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-17 17:06:07 -08:00
										 |  |  |     a.parse()  # Raises SyntaxError on failure | 
					
						
							| 
									
										
										
										
											2022-12-02 19:57:30 -08:00
										 |  |  |     a.analyze()  # Prints messages and sets a.errors on failure | 
					
						
							| 
									
										
										
										
											2022-11-17 17:06:07 -08:00
										 |  |  |     if a.errors: | 
					
						
							|  |  |  |         sys.exit(f"Found {a.errors} errors") | 
					
						
							| 
									
										
										
										
											2023-09-27 15:27:44 -07:00
										 |  |  |     if args.verbose: | 
					
						
							|  |  |  |         # Load execution counts from bmraw.json, if it exists | 
					
						
							|  |  |  |         a.report_non_viable_uops("bmraw.json") | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # These raise OSError if output can't be written | 
					
						
							|  |  |  |     a.write_instructions(args.output, args.emit_line_directives) | 
					
						
							| 
									
										
										
										
											2023-08-16 23:25:18 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     a.assign_opcode_ids() | 
					
						
							|  |  |  |     a.write_opcode_ids(args.opcode_ids_h, args.opcode_targets_h) | 
					
						
							| 
									
										
										
										
											2023-07-24 09:38:23 -07:00
										 |  |  |     a.write_metadata(args.metadata, args.pymetadata) | 
					
						
							|  |  |  |     a.write_executor_instructions(args.executor_cases, args.emit_line_directives) | 
					
						
							| 
									
										
										
										
											2023-08-18 22:42:45 +09:00
										 |  |  |     a.write_abstract_interpreter_instructions( | 
					
						
							|  |  |  |         args.abstract_interpreter_cases, args.emit_line_directives | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2022-11-02 21:31:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     main() |