Calculate basic block control-flow graph. If use_yields is set, then we treat returns inserted by yields as gotos instead of exits.
(blocks: list[BasicBlock], *, use_yields: bool = False)
| 83 | |
| 84 | |
| 85 | def get_cfg(blocks: list[BasicBlock], *, use_yields: bool = False) -> CFG: |
| 86 | """Calculate basic block control-flow graph. |
| 87 | |
| 88 | If use_yields is set, then we treat returns inserted by yields as gotos |
| 89 | instead of exits. |
| 90 | """ |
| 91 | succ_map = {} |
| 92 | pred_map: dict[BasicBlock, list[BasicBlock]] = {} |
| 93 | exits = set() |
| 94 | for block in blocks: |
| 95 | assert not any( |
| 96 | isinstance(op, ControlOp) for op in block.ops[:-1] |
| 97 | ), "Control-flow ops must be at the end of blocks" |
| 98 | |
| 99 | if use_yields and isinstance(block.terminator, Return) and block.terminator.yield_target: |
| 100 | succ = [block.terminator.yield_target] |
| 101 | else: |
| 102 | succ = list(block.terminator.targets()) |
| 103 | if not succ: |
| 104 | exits.add(block) |
| 105 | |
| 106 | # Errors can occur anywhere inside a block, which means that |
| 107 | # we can't assume that the entire block has executed before |
| 108 | # jumping to the error handler. In our CFG construction, we |
| 109 | # model this as saying that a block can jump to its error |
| 110 | # handler or the error handlers of any of its normal |
| 111 | # successors (to represent an error before that next block |
| 112 | # completes). This works well for analyses like "must |
| 113 | # defined", where it implies that registers assigned in a |
| 114 | # block may be undefined in its error handler, but is in |
| 115 | # general not a precise representation of reality; any |
| 116 | # analyses that require more fidelity must wait until after |
| 117 | # exception insertion. |
| 118 | for error_point in [block] + succ: |
| 119 | if error_point.error_handler: |
| 120 | succ.append(error_point.error_handler) |
| 121 | |
| 122 | succ_map[block] = succ |
| 123 | pred_map[block] = [] |
| 124 | for prev, nxt in succ_map.items(): |
| 125 | for label in nxt: |
| 126 | pred_map[label].append(prev) |
| 127 | return CFG(succ_map, pred_map, exits) |
| 128 | |
| 129 | |
| 130 | def get_real_target(label: BasicBlock) -> BasicBlock: |
no test coverage detected
searching dependent graphs…