Skip to content

Commit c01da28

Browse files
authored
gh-105481: refactor instr flag related code into a new InstructionFlags class (#105950)
1 parent 18a7c86 commit c01da28

File tree

1 file changed

+78
-36
lines changed

1 file changed

+78
-36
lines changed

Tools/cases_generator/generate_cases.py

+78-36
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,61 @@ def assign(self, dst: StackEffect, src: StackEffect):
234234
def cast(self, dst: StackEffect, src: StackEffect) -> str:
235235
return f"({dst.type or 'PyObject *'})" if src.type != dst.type else ""
236236

237-
INSTRUCTION_FLAGS = ['HAS_ARG', 'HAS_CONST', 'HAS_NAME', 'HAS_JUMP']
237+
@dataclasses.dataclass
238+
class InstructionFlags:
239+
"""Construct and manipulate instruction flags"""
240+
241+
HAS_ARG_FLAG: bool
242+
HAS_CONST_FLAG: bool
243+
HAS_NAME_FLAG: bool
244+
HAS_JUMP_FLAG: bool
245+
246+
def __post_init__(self):
247+
self.bitmask = {
248+
name : (1 << i) for i, name in enumerate(self.names())
249+
}
250+
251+
@staticmethod
252+
def fromInstruction(instr: "AnyInstruction"):
253+
return InstructionFlags(
254+
HAS_ARG_FLAG=variable_used(instr, "oparg"),
255+
HAS_CONST_FLAG=variable_used(instr, "FRAME_CO_CONSTS"),
256+
HAS_NAME_FLAG=variable_used(instr, "FRAME_CO_NAMES"),
257+
HAS_JUMP_FLAG=variable_used(instr, "JUMPBY"),
258+
)
259+
260+
@staticmethod
261+
def newEmpty():
262+
return InstructionFlags(False, False, False, False)
263+
264+
def add(self, other: "InstructionFlags") -> None:
265+
for name, value in dataclasses.asdict(other).items():
266+
if value:
267+
setattr(self, name, value)
268+
269+
def names(self, value=None):
270+
if value is None:
271+
return dataclasses.asdict(self).keys()
272+
return [n for n, v in dataclasses.asdict(self).items() if v == value]
273+
274+
def bitmap(self) -> int:
275+
flags = 0
276+
for name in self.names():
277+
if getattr(self, name):
278+
flags |= self.bitmask[name]
279+
return flags
280+
281+
@classmethod
282+
def emit_macros(cls, out: Formatter):
283+
flags = cls.newEmpty()
284+
for name, value in flags.bitmask.items():
285+
out.emit(f"#define {name} ({value})");
286+
287+
for name, value in flags.bitmask.items():
288+
out.emit(
289+
f"#define OPCODE_{name[:-len('_FLAG')]}(OP) "
290+
f"(_PyOpcode_opcode_metadata[(OP)].flags & ({name}))")
291+
238292

239293
@dataclasses.dataclass
240294
class Instruction:
@@ -256,7 +310,7 @@ class Instruction:
256310
output_effects: list[StackEffect]
257311
unmoved_names: frozenset[str]
258312
instr_fmt: str
259-
flags: int
313+
instr_flags: InstructionFlags
260314

261315
# Set later
262316
family: parser.Family | None = None
@@ -285,18 +339,10 @@ def __init__(self, inst: parser.InstDef):
285339
else:
286340
break
287341
self.unmoved_names = frozenset(unmoved_names)
288-
flag_data = {
289-
'HAS_ARG' : variable_used(inst, "oparg"),
290-
'HAS_CONST': variable_used(inst, "FRAME_CO_CONSTS"),
291-
'HAS_NAME' : variable_used(inst, "FRAME_CO_NAMES"),
292-
'HAS_JUMP' : variable_used(inst, "JUMPBY"),
293-
}
294-
assert set(flag_data.keys()) == set(INSTRUCTION_FLAGS)
295-
self.flags = 0
296-
for i, name in enumerate(INSTRUCTION_FLAGS):
297-
self.flags |= (1<<i) if flag_data[name] else 0
298342

299-
if flag_data['HAS_ARG']:
343+
self.instr_flags = InstructionFlags.fromInstruction(inst)
344+
345+
if self.instr_flags.HAS_ARG_FLAG:
300346
fmt = "IB"
301347
else:
302348
fmt = "IX"
@@ -495,7 +541,7 @@ class MacroInstruction:
495541
initial_sp: int
496542
final_sp: int
497543
instr_fmt: str
498-
flags: int
544+
instr_flags: InstructionFlags
499545
macro: parser.Macro
500546
parts: list[Component | parser.CacheEffect]
501547
predicted: bool = False
@@ -508,7 +554,7 @@ class PseudoInstruction:
508554
name: str
509555
targets: list[Instruction]
510556
instr_fmt: str
511-
flags: int
557+
instr_flags: InstructionFlags
512558

513559

514560
@dataclasses.dataclass
@@ -518,7 +564,6 @@ class OverriddenInstructionPlaceHolder:
518564

519565
AnyInstruction = Instruction | MacroInstruction | PseudoInstruction
520566
INSTR_FMT_PREFIX = "INSTR_FMT_"
521-
INSTR_FLAG_SUFFIX = "_FLAG"
522567

523568

524569
class Analyzer:
@@ -787,7 +832,7 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
787832
sp = initial_sp
788833
parts: list[Component | parser.CacheEffect] = []
789834
format = "IB"
790-
flags = 0
835+
flags = InstructionFlags.newEmpty()
791836
cache = "C"
792837
for component in components:
793838
match component:
@@ -803,7 +848,7 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
803848
for _ in range(ce.size):
804849
format += cache
805850
cache = "0"
806-
flags |= instr.flags
851+
flags.add(instr.instr_flags)
807852
case _:
808853
typing.assert_never(component)
809854
final_sp = sp
@@ -817,9 +862,8 @@ def analyze_pseudo(self, pseudo: parser.Pseudo) -> PseudoInstruction:
817862
# Make sure the targets have the same fmt
818863
fmts = list(set([t.instr_fmt for t in targets]))
819864
assert(len(fmts) == 1)
820-
flags_list = list(set([t.flags for t in targets]))
821-
assert(len(flags_list) == 1)
822-
return PseudoInstruction(pseudo.name, targets, fmts[0], flags_list[0])
865+
assert(len(list(set([t.instr_flags.bitmap() for t in targets]))) == 1)
866+
return PseudoInstruction(pseudo.name, targets, fmts[0], targets[0].instr_flags)
823867

824868
def analyze_instruction(
825869
self, instr: Instruction, stack: list[StackEffect], sp: int
@@ -1067,13 +1111,8 @@ def write_metadata(self) -> None:
10671111

10681112
# Write type definitions
10691113
self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};")
1070-
for i, flag in enumerate(INSTRUCTION_FLAGS):
1071-
self.out.emit(f"#define {flag}{INSTR_FLAG_SUFFIX} ({1 << i})");
1072-
for flag in INSTRUCTION_FLAGS:
1073-
flag_name = f"{flag}{INSTR_FLAG_SUFFIX}"
1074-
self.out.emit(
1075-
f"#define OPCODE_{flag}(OP) "
1076-
f"(_PyOpcode_opcode_metadata[(OP)].flags & ({flag_name}))")
1114+
1115+
InstructionFlags.emit_macros(self.out)
10771116

10781117
self.out.emit("struct opcode_metadata {")
10791118
with self.out.indent():
@@ -1153,25 +1192,28 @@ def write_pseudo_instrs(self) -> None:
11531192
self.out.emit(f" ((OP) == {op}) || \\")
11541193
self.out.emit(f" 0")
11551194

1156-
def emit_metadata_entry(self, name: str, fmt: str, flags: int) -> None:
1157-
flags_strs = [f"{name}{INSTR_FLAG_SUFFIX}"
1158-
for i, name in enumerate(INSTRUCTION_FLAGS) if (flags & (1<<i))]
1159-
flags_s = "0" if not flags_strs else ' | '.join(flags_strs)
1195+
def emit_metadata_entry(
1196+
self, name: str, fmt: str, flags: InstructionFlags
1197+
) -> None:
1198+
flag_names = flags.names(value=True)
1199+
if not flag_names:
1200+
flag_names.append("0")
11601201
self.out.emit(
1161-
f" [{name}] = {{ true, {INSTR_FMT_PREFIX}{fmt}, {flags_s} }},"
1202+
f" [{name}] = {{ true, {INSTR_FMT_PREFIX}{fmt},"
1203+
f" {' | '.join(flag_names)} }},"
11621204
)
11631205

11641206
def write_metadata_for_inst(self, instr: Instruction) -> None:
11651207
"""Write metadata for a single instruction."""
1166-
self.emit_metadata_entry(instr.name, instr.instr_fmt, instr.flags)
1208+
self.emit_metadata_entry(instr.name, instr.instr_fmt, instr.instr_flags)
11671209

11681210
def write_metadata_for_macro(self, mac: MacroInstruction) -> None:
11691211
"""Write metadata for a macro-instruction."""
1170-
self.emit_metadata_entry(mac.name, mac.instr_fmt, mac.flags)
1212+
self.emit_metadata_entry(mac.name, mac.instr_fmt, mac.instr_flags)
11711213

11721214
def write_metadata_for_pseudo(self, ps: PseudoInstruction) -> None:
11731215
"""Write metadata for a macro-instruction."""
1174-
self.emit_metadata_entry(ps.name, ps.instr_fmt, ps.flags)
1216+
self.emit_metadata_entry(ps.name, ps.instr_fmt, ps.instr_flags)
11751217

11761218
def write_instructions(self) -> None:
11771219
"""Write instructions to output file."""

0 commit comments

Comments
 (0)