Skip to content

Commit fa0df3e

Browse files
committed
Emit PREDICT() macros right before DISPATCH()
This should fix the build crash on Windows.
1 parent fdb0dbd commit fa0df3e

File tree

2 files changed

+59
-41
lines changed

2 files changed

+59
-41
lines changed

Python/generated_cases.c.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/cases_generator/generate_cases.py

Lines changed: 57 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
)
2424
BEGIN_MARKER = "// BEGIN BYTECODES //"
2525
END_MARKER = "// END BYTECODES //"
26-
RE_PREDICTED = r"(?s)(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);"
26+
RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*$"
2727
UNUSED = "unused"
2828
BITS_PER_CODE_UNIT = 16
2929

@@ -112,6 +112,8 @@ class Instruction:
112112
kind: typing.Literal["inst", "op"]
113113
name: str
114114
block: parser.Block
115+
block_text: list[str] # Block.text, less curlies, less PREDICT() calls
116+
predictions: list[str] # Prediction targets (instruction names)
115117

116118
# Computed by constructor
117119
always_exits: bool
@@ -129,7 +131,8 @@ def __init__(self, inst: parser.InstDef):
129131
self.kind = inst.kind
130132
self.name = inst.name
131133
self.block = inst.block
132-
self.always_exits = always_exits(self.block)
134+
self.block_text, self.predictions = extract_block_text(self.block)
135+
self.always_exits = always_exits(self.block_text)
133136
self.cache_effects = [
134137
effect for effect in inst.inputs if isinstance(effect, parser.CacheEffect)
135138
]
@@ -164,15 +167,15 @@ def write(self, out: Formatter) -> None:
164167
self.write_body(out, 0)
165168

166169
# Skip the rest if the block always exits
167-
if always_exits(self.block):
170+
if self.always_exits:
168171
return
169172

170173
# Write net stack growth/shrinkage
171174
diff = len(self.output_effects) - len(self.input_effects)
172175
out.stack_adjust(diff)
173176

174177
# Write output stack effect assignments
175-
unmoved_names = set()
178+
unmoved_names: set[str] = set()
176179
for ieffect, oeffect in zip(self.input_effects, self.output_effects):
177180
if ieffect.name == oeffect.name:
178181
unmoved_names.add(ieffect.name)
@@ -206,27 +209,10 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
206209
cache_offset += ceffect.size
207210
assert cache_offset == self.cache_offset + cache_adjust
208211

209-
# Get lines of text with proper dedent
210-
blocklines = self.block.to_text(dedent=dedent).splitlines(True)
211-
212-
# Remove blank lines from both ends
213-
while blocklines and not blocklines[0].strip():
214-
blocklines.pop(0)
215-
while blocklines and not blocklines[-1].strip():
216-
blocklines.pop()
217-
218-
# Remove leading and trailing braces
219-
assert blocklines and blocklines[0].strip() == "{"
220-
assert blocklines and blocklines[-1].strip() == "}"
221-
blocklines.pop()
222-
blocklines.pop(0)
223-
224-
# Remove trailing blank lines
225-
while blocklines and not blocklines[-1].strip():
226-
blocklines.pop()
227-
228212
# Write the body, substituting a goto for ERROR_IF()
229-
for line in blocklines:
213+
assert dedent <= 0
214+
extra = " " * -dedent
215+
for line in self.block_text:
230216
if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*$", line):
231217
space, cond, label = m.groups()
232218
# ERROR_IF() must pop the inputs from the stack.
@@ -241,11 +227,13 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
241227
else:
242228
break
243229
if ninputs:
244-
out.write_raw(f"{space}if ({cond}) goto pop_{ninputs}_{label};\n")
230+
out.write_raw(
231+
f"{extra}{space}if ({cond}) goto pop_{ninputs}_{label};\n"
232+
)
245233
else:
246-
out.write_raw(f"{space}if ({cond}) goto {label};\n")
234+
out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n")
247235
else:
248-
out.write_raw(line)
236+
out.write_raw(extra + line)
249237

250238

251239
InstructionOrCacheEffect = Instruction | parser.CacheEffect
@@ -395,7 +383,11 @@ def analyze(self) -> None:
395383
def find_predictions(self) -> None:
396384
"""Find the instructions that need PREDICTED() labels."""
397385
for instr in self.instrs.values():
398-
for target in re.findall(RE_PREDICTED, instr.block.text):
386+
targets = set(instr.predictions)
387+
for line in instr.block_text:
388+
if m := re.match(RE_PREDICTED, line):
389+
targets.add(m.group(1))
390+
for target in targets:
399391
if target_instr := self.instrs.get(target):
400392
target_instr.predicted = True
401393
else:
@@ -552,7 +544,9 @@ def stack_analysis(
552544
# and 'lowest' and 'highest' are the extremes.
553545
# Note that 'lowest' may be negative.
554546
# TODO: Reverse the numbering.
555-
stack = [StackEffect(f"_tmp_{i+1}", "") for i in reversed(range(highest - lowest))]
547+
stack = [
548+
StackEffect(f"_tmp_{i+1}", "") for i in reversed(range(highest - lowest))
549+
]
556550
return stack, -lowest
557551

558552
def write_instructions(self) -> None:
@@ -577,7 +571,9 @@ def write_instructions(self) -> None:
577571
if instr.predicted:
578572
self.out.emit(f"PREDICTED({name});")
579573
instr.write(self.out)
580-
if not always_exits(instr.block):
574+
if not instr.always_exits:
575+
for prediction in instr.predictions:
576+
self.out.emit(f"PREDICT({prediction});")
581577
self.out.emit(f"DISPATCH();")
582578

583579
# Write and count super-instructions
@@ -652,18 +648,40 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
652648
self.out.emit(f"DISPATCH();")
653649

654650

655-
def always_exits(block: parser.Block) -> bool:
651+
def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
652+
# Get lines of text with proper dedent
653+
blocklines = block.text.splitlines(True)
654+
655+
# Remove blank lines from both ends
656+
while blocklines and not blocklines[0].strip():
657+
blocklines.pop(0)
658+
while blocklines and not blocklines[-1].strip():
659+
blocklines.pop()
660+
661+
# Remove leading and trailing braces
662+
assert blocklines and blocklines[0].strip() == "{"
663+
assert blocklines and blocklines[-1].strip() == "}"
664+
blocklines.pop()
665+
blocklines.pop(0)
666+
667+
# Remove trailing blank lines
668+
while blocklines and not blocklines[-1].strip():
669+
blocklines.pop()
670+
671+
# Separate PREDICT(...) macros from end
672+
predictions: list[str] = []
673+
while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*$", blocklines[-1])):
674+
predictions.insert(0, m.group(1))
675+
blocklines.pop()
676+
677+
return blocklines, predictions
678+
679+
680+
def always_exits(lines: list[str]) -> bool:
656681
"""Determine whether a block always ends in a return/goto/etc."""
657-
text = block.text
658-
lines = text.splitlines()
659-
while lines and not lines[-1].strip():
660-
lines.pop()
661-
if not lines or lines[-1].strip() != "}":
662-
return False
663-
lines.pop()
664682
if not lines:
665683
return False
666-
line = lines.pop().rstrip()
684+
line = lines[-1].rstrip()
667685
# Indent must match exactly (TODO: Do something better)
668686
if line[:12] != " " * 12:
669687
return False

0 commit comments

Comments
 (0)