Skip to content

Commit 5e33d90

Browse files
DinoVfacebook-github-bot
authored andcommitted
Fix code gen for RESUME and YIELD_VALUE
Summary: `RESUME` now has a mask that can be applied, and 'YIELD_VALUE` no longer gets set the length of the exception stack. Reviewed By: martindemello Differential Revision: D80820416 fbshipit-source-id: 462a4e5a672cbd770ba1f57229c46b40982549b0
1 parent 2075b94 commit 5e33d90

2 files changed

Lines changed: 79 additions & 9 deletions

File tree

cinderx/PythonLib/cinderx/compiler/pyassem.py

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from collections import defaultdict
1010
from contextlib import contextmanager, redirect_stdout
1111
from dataclasses import dataclass
12-
from enum import IntEnum
12+
from enum import IntEnum, IntFlag
1313

1414
try:
1515
# pyre-ignore[21]: No _inline_cache_entries
@@ -54,6 +54,16 @@
5454
MAX_COPY_SIZE = 4
5555

5656

57+
class ResumeOparg(IntFlag):
58+
ScopeEntry = 0
59+
Yield = 1
60+
YieldFrom = 2
61+
Await = 3
62+
63+
LocationMask = 0x03
64+
Depth1Mask = 0x04
65+
66+
5767
def sign(a: float) -> float:
5868
if not isinstance(a, float):
5969
raise TypeError(f"Must be a real number, not {type(a)}")
@@ -2700,6 +2710,66 @@ def make_explicit_jump_block(self) -> Block:
27002710
res.num_predecessors = 1
27012711
return res
27022712

2713+
def label_exception_targets(self) -> None:
2714+
def push_todo_block(block: Block) -> None:
2715+
todo_stack.append(block)
2716+
visited.add(block)
2717+
2718+
todo_stack: list[Block] = [self.entry]
2719+
visited: set[Block] = {self.entry}
2720+
except_stack = []
2721+
self.entry.except_stack = except_stack
2722+
2723+
while todo_stack:
2724+
block = todo_stack.pop()
2725+
assert block in visited
2726+
except_stack = block.except_stack
2727+
block.except_stack = []
2728+
handler = except_stack[-1] if except_stack else None
2729+
last_yield_except_depth = -1
2730+
for instr in block.insts:
2731+
if instr.opname in SETUP_OPCODES:
2732+
target = instr.target
2733+
assert target, instr
2734+
if target not in visited:
2735+
# Copy the current except stack into the target's except stack
2736+
target.except_stack = list(except_stack)
2737+
push_todo_block(target)
2738+
handler = self.push_except_block(except_stack, instr)
2739+
elif instr.opname == "POP_BLOCK":
2740+
except_stack.pop()
2741+
handler = except_stack[-1] if except_stack else None
2742+
instr.set_to_nop()
2743+
elif instr.is_jump(self.opcode) and instr.opname != "END_ASYNC_FOR":
2744+
instr.exc_handler = handler
2745+
if instr.target not in visited:
2746+
target = instr.target
2747+
assert target
2748+
if block.has_fallthrough:
2749+
# Copy the current except stack into the block's except stack
2750+
target.except_stack = list(except_stack)
2751+
else:
2752+
# Move the current except stack to the block and start a new one
2753+
target.except_stack = except_stack
2754+
except_stack = []
2755+
push_todo_block(target)
2756+
elif instr.opname == "YIELD_VALUE":
2757+
last_yield_except_depth = len(except_stack)
2758+
instr.exc_handler = handler
2759+
elif instr.opname == "RESUME":
2760+
instr.exc_handler = handler
2761+
if instr.oparg != ResumeOparg.ScopeEntry:
2762+
if last_yield_except_depth == 1:
2763+
instr.ioparg |= ResumeOparg.Depth1Mask
2764+
last_yield_except_depth = -1
2765+
else:
2766+
instr.exc_handler = handler
2767+
2768+
if block.has_fallthrough and block.next and block.next not in visited:
2769+
assert except_stack is not None
2770+
block.next.except_stack = except_stack
2771+
push_todo_block(block.next)
2772+
27032773
_const_opcodes: set[str] = set(PyFlowGraph312._const_opcodes) | {"LOAD_SMALL_INT"}
27042774

27052775

cinderx/PythonLib/cinderx/compiler/pycodegen.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
PyFlowGraph314,
6262
PyFlowGraphCinder310,
6363
PyFlowGraphCinder312,
64+
ResumeOparg,
6465
SrcLocation,
6566
)
6667
from .symbols import (
@@ -2748,13 +2749,6 @@ def __init__(
27482749
self.node = node
27492750

27502751

2751-
class ResumeOparg(IntEnum):
2752-
ScopeEntry = 0
2753-
Yield = 1
2754-
YieldFrom = 2
2755-
Await = 3
2756-
2757-
27582752
class CodeGenerator310(CodeGenerator):
27592753
flow_graph: type[PyFlowGraph] = PyFlowGraph310
27602754
_SymbolVisitor = SymbolVisitor310
@@ -4309,6 +4303,9 @@ def emit_print(self) -> None:
43094303
self.set_no_pos()
43104304
self.emit("POP_TOP")
43114305

4306+
def emit_yield_value_for_yield_from(self) -> None:
4307+
self.emit("YIELD_VALUE")
4308+
43124309
def emit_yield_from(self, await_: bool = False) -> None:
43134310
send = self.newBlock("send")
43144311
fail = self.newBlock("fail")
@@ -4321,7 +4318,7 @@ def emit_yield_from(self, await_: bool = False) -> None:
43214318

43224319
# Setup try/except to handle StopIteration
43234320
self.emit("SETUP_FINALLY", fail)
4324-
self.emit("YIELD_VALUE")
4321+
self.emit_yield_value_for_yield_from()
43254322
self.emit_noline("POP_BLOCK")
43264323
self.emit_resume(ResumeOparg.Await if await_ else ResumeOparg.YieldFrom)
43274324
self.emit("JUMP_NO_INTERRUPT", send)
@@ -6376,6 +6373,9 @@ def visitWhile(self, node: ast.While) -> None:
63766373

63776374
self.nextBlock(after)
63786375

6376+
def emit_yield_value_for_yield_from(self) -> None:
6377+
self.emit("YIELD_VALUE", 1)
6378+
63796379

63806380
class CinderCodeGenerator310(CinderCodeGenBase, CodeGenerator310):
63816381
flow_graph = PyFlowGraphCinder310

0 commit comments

Comments
 (0)