Skip to content

Commit f76919e

Browse files
DinoVfacebook-github-bot
authored andcommitted
Compute cold basic blocks
Summary: Previously we were able to skip calculation of cold basic blocks as they were identical to not warm blocks. But with the preservation of non-empty blocks in 3.14 this has changed and blocks can end up both not warm and not cold. We therefore need to calculate cold blocks now too. Reviewed By: mpage Differential Revision: D82127010 fbshipit-source-id: fc5f6c0f1981aaea6ad9472b4c236ecaae2127bd
1 parent 33bde72 commit f76919e

2 files changed

Lines changed: 110 additions & 2 deletions

File tree

cinderx/PythonLib/cinderx/compiler/pyassem.py

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,8 @@ def __repr__(self) -> str:
418418
data.append(f"startdepth={self.startdepth}")
419419
if self.next:
420420
data.append(f"next={self.next.bid}")
421+
if self.is_exc_handler:
422+
data.append("EH")
421423
extras = ", ".join(data)
422424
if self.label:
423425
return f"<block {self.label} {extras}>"
@@ -2356,6 +2358,103 @@ def flatten_jump(self, inst: Instruction, pc: int) -> int:
23562358

23572359
return res
23582360

2361+
def mark_except_handlers(self) -> None:
2362+
for block in self.ordered_blocks:
2363+
for instr in block.insts:
2364+
if instr.opname in SETUP_OPCODES:
2365+
target = instr.target
2366+
assert target is not None, "SETUP_* opcodes all have targets"
2367+
target.is_exc_handler = True
2368+
break
2369+
2370+
def push_cold_blocks_to_end(
2371+
self, except_handlers: set[Block] | None, optimizer: FlowGraphOptimizer
2372+
) -> None:
2373+
warm = self.compute_warm()
2374+
cold = self.compute_cold(warm)
2375+
2376+
# If we have a cold block with fallthrough to a warm block, add
2377+
# an explicit jump instead of fallthrough
2378+
for block in list(self.ordered_blocks):
2379+
if block in cold and block.has_fallthrough and block.next in warm:
2380+
explicit_jump = self.make_explicit_jump_block()
2381+
explicit_jump.bid = self.get_new_block_id()
2382+
explicit_jump.num_predecessors = 1
2383+
cold.add(explicit_jump)
2384+
self.current = explicit_jump
2385+
2386+
next_block = block.next
2387+
assert next_block is not None
2388+
2389+
self.emit_jump_forward_noline(next_block)
2390+
self.ordered_blocks.insert(
2391+
self.ordered_blocks.index(block) + 1, explicit_jump
2392+
)
2393+
2394+
explicit_jump.next = block.next
2395+
explicit_jump.has_fallthrough = False
2396+
block.next = explicit_jump
2397+
2398+
new_ordered = []
2399+
to_end = []
2400+
prev = None
2401+
for block in self.ordered_blocks:
2402+
if block in warm:
2403+
new_ordered.append(block)
2404+
if prev is not None:
2405+
prev.next = block
2406+
block.prev = prev
2407+
prev = block
2408+
else:
2409+
to_end.append(block)
2410+
2411+
for block in to_end:
2412+
prev.next = block
2413+
block.prev = prev
2414+
prev = block
2415+
2416+
block.next = None
2417+
2418+
self.ordered_blocks = new_ordered + to_end
2419+
if to_end:
2420+
self.remove_redundant_nops_and_jumps(optimizer)
2421+
2422+
def has_fallthrough(self, block: Block) -> bool:
2423+
if not block.insts:
2424+
return True
2425+
2426+
last = block.insts[-1]
2427+
return (
2428+
last.opname not in UNCONDITIONAL_JUMP_OPCODES
2429+
and last.opname not in SCOPE_EXIT_OPCODES
2430+
)
2431+
2432+
def compute_cold(self, warm: set[Block]) -> set[Block]:
2433+
stack = []
2434+
cold = set()
2435+
visited = set()
2436+
2437+
for block in self.ordered_blocks:
2438+
if block.is_exc_handler:
2439+
assert block not in warm
2440+
stack.append(block)
2441+
2442+
for block in stack:
2443+
cold.add(block)
2444+
next = block.next
2445+
if next is not None and self.has_fallthrough(block):
2446+
if next not in warm and next not in visited:
2447+
stack.append(next)
2448+
visited.add(next)
2449+
2450+
for instr in block.insts:
2451+
if instr.is_jump(self.opcode):
2452+
target = instr.target
2453+
if target not in warm and target not in visited:
2454+
stack.append(target)
2455+
visited.add(target)
2456+
return cold
2457+
23592458
# The following bits are chosen so that the value of
23602459
# COMPARSION_BIT(left, right)
23612460
# masked by the values below will be non-zero if the
@@ -3025,7 +3124,7 @@ def remove_redundant_nops_and_jumps(self, optimizer: FlowGraphOptimizer) -> None
30253124

30263125
def optimizeCFG(self) -> None:
30273126
"""Optimize a well-formed CFG."""
3028-
except_handlers = self.compute_except_handlers()
3127+
self.mark_except_handlers()
30293128

30303129
self.label_exception_targets()
30313130

@@ -3059,7 +3158,7 @@ def optimizeCFG(self) -> None:
30593158
self.remove_unused_consts()
30603159
self.add_checks_for_loads_of_uninitialized_variables()
30613160
self.insert_superinstructions()
3062-
self.push_cold_blocks_to_end(except_handlers, optimizer)
3161+
self.push_cold_blocks_to_end(None, optimizer)
30633162
self.propagate_line_numbers()
30643163

30653164
_const_opcodes: set[str] = set(PyFlowGraph312._const_opcodes) | {"LOAD_SMALL_INT"}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
async def f():
2+
match b'':
3+
case True:
4+
pass
5+
case name_5 if f'e':
6+
{name_3: name_4 async for name_2 in name_5}
7+
case []:
8+
pass
9+
[[]]

0 commit comments

Comments
 (0)