From c01da2896ab92ba7193bcd6ae56908c5c7277e75 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Thu, 22 Jun 2023 00:14:43 +0100 Subject: [PATCH] gh-105481: refactor instr flag related code into a new InstructionFlags class (#105950) --- Tools/cases_generator/generate_cases.py | 114 ++++++++++++++++-------- 1 file changed, 78 insertions(+), 36 deletions(-) diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index e4ccd38bac4..1afdeef41f0 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -234,7 +234,61 @@ class Formatter: def cast(self, dst: StackEffect, src: StackEffect) -> str: return f"({dst.type or 'PyObject *'})" if src.type != dst.type else "" -INSTRUCTION_FLAGS = ['HAS_ARG', 'HAS_CONST', 'HAS_NAME', 'HAS_JUMP'] +@dataclasses.dataclass +class InstructionFlags: + """Construct and manipulate instruction flags""" + + HAS_ARG_FLAG: bool + HAS_CONST_FLAG: bool + HAS_NAME_FLAG: bool + HAS_JUMP_FLAG: bool + + def __post_init__(self): + self.bitmask = { + name : (1 << i) for i, name in enumerate(self.names()) + } + + @staticmethod + def fromInstruction(instr: "AnyInstruction"): + return InstructionFlags( + HAS_ARG_FLAG=variable_used(instr, "oparg"), + HAS_CONST_FLAG=variable_used(instr, "FRAME_CO_CONSTS"), + HAS_NAME_FLAG=variable_used(instr, "FRAME_CO_NAMES"), + HAS_JUMP_FLAG=variable_used(instr, "JUMPBY"), + ) + + @staticmethod + def newEmpty(): + return InstructionFlags(False, False, False, False) + + def add(self, other: "InstructionFlags") -> None: + for name, value in dataclasses.asdict(other).items(): + if value: + setattr(self, name, value) + + def names(self, value=None): + if value is None: + return dataclasses.asdict(self).keys() + return [n for n, v in dataclasses.asdict(self).items() if v == value] + + def bitmap(self) -> int: + flags = 0 + for name in self.names(): + if getattr(self, name): + flags |= self.bitmask[name] + return flags + + @classmethod + def emit_macros(cls, out: Formatter): + flags = cls.newEmpty() + for name, value in flags.bitmask.items(): + out.emit(f"#define {name} ({value})"); + + for name, value in flags.bitmask.items(): + out.emit( + f"#define OPCODE_{name[:-len('_FLAG')]}(OP) " + f"(_PyOpcode_opcode_metadata[(OP)].flags & ({name}))") + @dataclasses.dataclass class Instruction: @@ -256,7 +310,7 @@ class Instruction: output_effects: list[StackEffect] unmoved_names: frozenset[str] instr_fmt: str - flags: int + instr_flags: InstructionFlags # Set later family: parser.Family | None = None @@ -285,18 +339,10 @@ class Instruction: else: break self.unmoved_names = frozenset(unmoved_names) - flag_data = { - 'HAS_ARG' : variable_used(inst, "oparg"), - 'HAS_CONST': variable_used(inst, "FRAME_CO_CONSTS"), - 'HAS_NAME' : variable_used(inst, "FRAME_CO_NAMES"), - 'HAS_JUMP' : variable_used(inst, "JUMPBY"), - } - assert set(flag_data.keys()) == set(INSTRUCTION_FLAGS) - self.flags = 0 - for i, name in enumerate(INSTRUCTION_FLAGS): - self.flags |= (1< None: - flags_strs = [f"{name}{INSTR_FLAG_SUFFIX}" - for i, name in enumerate(INSTRUCTION_FLAGS) if (flags & (1< None: + flag_names = flags.names(value=True) + if not flag_names: + flag_names.append("0") self.out.emit( - f" [{name}] = {{ true, {INSTR_FMT_PREFIX}{fmt}, {flags_s} }}," + f" [{name}] = {{ true, {INSTR_FMT_PREFIX}{fmt}," + f" {' | '.join(flag_names)} }}," ) def write_metadata_for_inst(self, instr: Instruction) -> None: """Write metadata for a single instruction.""" - self.emit_metadata_entry(instr.name, instr.instr_fmt, instr.flags) + self.emit_metadata_entry(instr.name, instr.instr_fmt, instr.instr_flags) def write_metadata_for_macro(self, mac: MacroInstruction) -> None: """Write metadata for a macro-instruction.""" - self.emit_metadata_entry(mac.name, mac.instr_fmt, mac.flags) + self.emit_metadata_entry(mac.name, mac.instr_fmt, mac.instr_flags) def write_metadata_for_pseudo(self, ps: PseudoInstruction) -> None: """Write metadata for a macro-instruction.""" - self.emit_metadata_entry(ps.name, ps.instr_fmt, ps.flags) + self.emit_metadata_entry(ps.name, ps.instr_fmt, ps.instr_flags) def write_instructions(self) -> None: """Write instructions to output file."""