Skip to content

Commit 6d532c7

Browse files
iritkatrielgvanrossum
authored andcommitted
pythongh-105481: refactor instr flag related code into a new InstructionFlags class (python#105950)
1 parent 71ff09c commit 6d532c7

File tree

1 file changed

+78
-38
lines changed

1 file changed

+78
-38
lines changed

Tools/cases_generator/generate_cases.py

Lines changed: 78 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,61 @@ def assign(self, dst: StackEffect, src: StackEffect):
244244
def cast(self, dst: StackEffect, src: StackEffect) -> str:
245245
return f"({dst.type or 'PyObject *'})" if src.type != dst.type else ""
246246

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

249303
FORBIDDEN_INSTRUCTIONS = (
250304
"cframe",
@@ -280,8 +334,7 @@ class Instruction:
280334
output_effects: list[StackEffect]
281335
unmoved_names: frozenset[str]
282336
instr_fmt: str
283-
flags: int
284-
flag_data: dict[str, bool]
337+
instr_flags: InstructionFlags
285338

286339
# Set later
287340
family: parser.Family | None = None
@@ -310,19 +363,10 @@ def __init__(self, inst: parser.InstDef):
310363
else:
311364
break
312365
self.unmoved_names = frozenset(unmoved_names)
313-
self.flag_data = {
314-
'HAS_ARG' : variable_used(inst, "oparg"),
315-
'HAS_CONST': variable_used(inst, "FRAME_CO_CONSTS"),
316-
'HAS_NAME' : variable_used(inst, "FRAME_CO_NAMES"),
317-
'HAS_JUMP' : variable_used(inst, "JUMPBY"),
318-
'IS_UOP' : self.is_viable_uop(),
319-
}
320-
assert set(self.flag_data.keys()) == set(INSTRUCTION_FLAGS)
321-
self.flags = 0
322-
for i, name in enumerate(INSTRUCTION_FLAGS):
323-
self.flags |= (1<<i) if self.flag_data[name] else 0
324366

325-
if self.flag_data['HAS_ARG']:
367+
self.instr_flags = InstructionFlags.fromInstruction(inst)
368+
369+
if self.instr_flags.HAS_ARG_FLAG:
326370
fmt = "IB"
327371
else:
328372
fmt = "IX"
@@ -544,7 +588,7 @@ class MacroInstruction:
544588
initial_sp: int
545589
final_sp: int
546590
instr_fmt: str
547-
flags: int
591+
instr_flags: InstructionFlags
548592
macro: parser.Macro
549593
parts: list[Component | parser.CacheEffect]
550594
predicted: bool = False
@@ -557,7 +601,7 @@ class PseudoInstruction:
557601
name: str
558602
targets: list[Instruction]
559603
instr_fmt: str
560-
flags: int
604+
instr_flags: InstructionFlags
561605

562606

563607
@dataclasses.dataclass
@@ -567,7 +611,6 @@ class OverriddenInstructionPlaceHolder:
567611

568612
AnyInstruction = Instruction | MacroInstruction | PseudoInstruction
569613
INSTR_FMT_PREFIX = "INSTR_FMT_"
570-
INSTR_FLAG_SUFFIX = "_FLAG"
571614

572615

573616
class Analyzer:
@@ -839,7 +882,7 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
839882
sp = initial_sp
840883
parts: list[Component | parser.CacheEffect] = []
841884
format = "IB"
842-
flags = 0
885+
flags = InstructionFlags.newEmpty()
843886
cache = "C"
844887
for component in components:
845888
match component:
@@ -855,7 +898,7 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
855898
for _ in range(ce.size):
856899
format += cache
857900
cache = "0"
858-
flags |= instr.flags & 0x0F # Strip out IS_UOP
901+
flags.add(instr.instr_flags)
859902
case _:
860903
typing.assert_never(component)
861904
final_sp = sp
@@ -869,9 +912,8 @@ def analyze_pseudo(self, pseudo: parser.Pseudo) -> PseudoInstruction:
869912
# Make sure the targets have the same fmt
870913
fmts = list(set([t.instr_fmt for t in targets]))
871914
assert(len(fmts) == 1)
872-
flags_list = list(set([t.flags for t in targets]))
873-
assert(len(flags_list) == 1)
874-
return PseudoInstruction(pseudo.name, targets, fmts[0], flags_list[0])
915+
assert(len(list(set([t.instr_flags.bitmap() for t in targets]))) == 1)
916+
return PseudoInstruction(pseudo.name, targets, fmts[0], targets[0].instr_flags)
875917

876918
def analyze_instruction(
877919
self, instr: Instruction, stack: list[StackEffect], sp: int
@@ -1119,13 +1161,8 @@ def write_metadata(self) -> None:
11191161

11201162
# Write type definitions
11211163
self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};")
1122-
for i, flag in enumerate(INSTRUCTION_FLAGS):
1123-
self.out.emit(f"#define {flag}{INSTR_FLAG_SUFFIX} ({1 << i})");
1124-
for flag in INSTRUCTION_FLAGS:
1125-
flag_name = f"{flag}{INSTR_FLAG_SUFFIX}"
1126-
self.out.emit(
1127-
f"#define OPCODE_{flag}(OP) "
1128-
f"(_PyOpcode_opcode_metadata[(OP)].flags & ({flag_name}))")
1164+
1165+
InstructionFlags.emit_macros(self.out)
11291166

11301167
self.out.emit("struct opcode_metadata {")
11311168
with self.out.indent():
@@ -1205,25 +1242,28 @@ def write_pseudo_instrs(self) -> None:
12051242
self.out.emit(f" ((OP) == {op}) || \\")
12061243
self.out.emit(f" 0")
12071244

1208-
def emit_metadata_entry(self, name: str, fmt: str, flags: int) -> None:
1209-
flags_strs = [f"{name}{INSTR_FLAG_SUFFIX}"
1210-
for i, name in enumerate(INSTRUCTION_FLAGS) if (flags & (1<<i))]
1211-
flags_s = "0" if not flags_strs else ' | '.join(flags_strs)
1245+
def emit_metadata_entry(
1246+
self, name: str, fmt: str, flags: InstructionFlags
1247+
) -> None:
1248+
flag_names = flags.names(value=True)
1249+
if not flag_names:
1250+
flag_names.append("0")
12121251
self.out.emit(
1213-
f" [{name}] = {{ true, {INSTR_FMT_PREFIX}{fmt}, {flags_s} }},"
1252+
f" [{name}] = {{ true, {INSTR_FMT_PREFIX}{fmt},"
1253+
f" {' | '.join(flag_names)} }},"
12141254
)
12151255

12161256
def write_metadata_for_inst(self, instr: Instruction) -> None:
12171257
"""Write metadata for a single instruction."""
1218-
self.emit_metadata_entry(instr.name, instr.instr_fmt, instr.flags)
1258+
self.emit_metadata_entry(instr.name, instr.instr_fmt, instr.instr_flags)
12191259

12201260
def write_metadata_for_macro(self, mac: MacroInstruction) -> None:
12211261
"""Write metadata for a macro-instruction."""
1222-
self.emit_metadata_entry(mac.name, mac.instr_fmt, mac.flags)
1262+
self.emit_metadata_entry(mac.name, mac.instr_fmt, mac.instr_flags)
12231263

12241264
def write_metadata_for_pseudo(self, ps: PseudoInstruction) -> None:
12251265
"""Write metadata for a macro-instruction."""
1226-
self.emit_metadata_entry(ps.name, ps.instr_fmt, ps.flags)
1266+
self.emit_metadata_entry(ps.name, ps.instr_fmt, ps.instr_flags)
12271267

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

0 commit comments

Comments
 (0)