def _start_new_block(self, offset): oldblock = self.current_block self.insert_block(offset) # Ensure the last block is terminated if oldblock is not None and not oldblock.is_terminated: # Handle ending try block. tryblk = self.dfainfo.active_try_block # If there's an active try-block and the handler block is live. if tryblk is not None and tryblk["end"] in self.cfa.graph.nodes(): # We are in a try-block, insert a branch to except-block. # This logic cannot be in self._end_current_block() # because we the non-raising next block-offset. branch = ir.Branch( cond=self.get("$exception_check"), truebr=tryblk["end"], falsebr=offset, loc=self.loc, ) oldblock.append(branch) # Handle normal case else: jmp = ir.Jump(offset, loc=self.loc) oldblock.append(jmp) # Get DFA block info self.dfainfo = self.dfa.infos[self.current_block_offset] self.assigner = Assigner() # Check out-of-scope syntactic-block while self.syntax_blocks: if offset >= self.syntax_blocks[-1].exit: self.syntax_blocks.pop() else: break
def op_FOR_ITER(self, inst, iterator, pair, indval, pred): """ Assign new block other this instruction. """ assert inst.offset in self.blocks, "FOR_ITER must be block head" # Emit code val = self.get(iterator) pairval = ir.Expr.iternext(value=val, loc=self.loc) self.store(pairval, pair) iternext = ir.Expr.pair_first(value=self.get(pair), loc=self.loc) self.store(iternext, indval) isvalid = ir.Expr.pair_second(value=self.get(pair), loc=self.loc) self.store(isvalid, pred) # Conditional jump br = ir.Branch( cond=self.get(pred), truebr=inst.next, falsebr=inst.get_jump_target(), loc=self.loc, ) self.current_block.append(br)
def _op_JUMP_IF(self, inst, pred, iftrue): brs = { True: inst.get_jump_target(), False: inst.next, } truebr = brs[iftrue] falsebr = brs[not iftrue] bra = ir.Branch(cond=self.get(pred), truebr=truebr, falsebr=falsebr, loc=self.loc) self.current_block.append(bra)
def test_branch(self): a = ir.Branch(self.var_a, 1, 2, self.loc1) b = ir.Branch(self.var_a, 1, 2, self.loc1) c = ir.Branch(self.var_a, 1, 2, self.loc2) d = ir.Branch(self.var_b, 1, 2, self.loc1) e = ir.Branch(self.var_a, 2, 2, self.loc1) f = ir.Branch(self.var_a, 1, 3, self.loc1) self.check(a, same=[b, c], different=[d, e, f])
def replace_target(term, src, dst): def replace(target): return (dst if target == src else target) if isinstance(term, ir.Branch): return ir.Branch(cond=term.cond, truebr=replace(term.truebr), falsebr=replace(term.falsebr), loc=term.loc) elif isinstance(term, ir.Jump): return ir.Jump(target=replace(term.target), loc=term.loc) else: assert not term.get_targets() return term
def _op_JUMP_IF(self, inst, pred, iftrue): brs = { True: inst.get_jump_target(), False: inst.next, } truebr = brs[iftrue] falsebr = brs[not iftrue] name = "bool%s" % (inst.offset) gv_fn = ir.Global("bool", bool, loc=self.loc) self.store(value=gv_fn, name=name) callres = ir.Expr.call(self.get(name), (self.get(pred),), (), loc=self.loc) pname = "$%spred" % (inst.offset) predicate = self.store(value=callres, name=pname) bra = ir.Branch(cond=predicate, truebr=truebr, falsebr=falsebr, loc=self.loc) self.current_block.append(bra)
def _fix_multi_exit_blocks(func_ir, exit_nodes, *, split_condition=None): """Modify the FunctionIR to create a single common exit node given the original exit nodes. Parameters ---------- func_ir : The FunctionIR. Mutated inplace. exit_nodes : The original exit nodes. A sequence of block keys. split_condition : callable or None If not None, it is a callable with the signature `split_condition(statement)` that determines if the `statement` is the splitting point (e.g. `POP_BLOCK`) in an exit node. If it's None, the exit node is not split. """ # Convert the following: # # | | # +-------+ +-------+ # | exit0 | | exit1 | # +-------+ +-------+ # | | # +-------+ +-------+ # | after0| | after1| # +-------+ +-------+ # | | # # To roughly: # # | | # +-------+ +-------+ # | exit0 | | exit1 | # +-------+ +-------+ # | | # +-----+-----+ # | # +---------+ # | common | # +---------+ # | # +-------+ # | post | # +-------+ # | # +-----+-----+ # | | # +-------+ +-------+ # | after0| | after1| # +-------+ +-------+ blocks = func_ir.blocks # Getting the scope any_blk = min(func_ir.blocks.values()) scope = any_blk.scope # Getting the maximum block label max_label = max(func_ir.blocks) + 1 # Define the new common block for the new exit. common_block = ir.Block(any_blk.scope, loc=ir.unknown_loc) common_label = max_label max_label += 1 blocks[common_label] = common_block # Define the new block after the exit. post_block = ir.Block(any_blk.scope, loc=ir.unknown_loc) post_label = max_label max_label += 1 blocks[post_label] = post_block # Adjust each exit node remainings = [] for i, k in enumerate(exit_nodes): blk = blocks[k] # split the block if needed if split_condition is not None: for pt, stmt in enumerate(blk.body): if split_condition(stmt): break else: # no splitting pt = -1 before = blk.body[:pt] after = blk.body[pt:] remainings.append(after) # Add control-point variable to mark which exit block this is. blk.body = before loc = blk.loc blk.body.append( ir.Assign(value=ir.Const(i, loc=loc), target=scope.get_or_define("$cp", loc=loc), loc=loc)) # Replace terminator with a jump to the common block assert not blk.is_terminated blk.body.append(ir.Jump(common_label, loc=ir.unknown_loc)) if split_condition is not None: # Move the splitting statement to the common block common_block.body.append(remainings[0][0]) assert not common_block.is_terminated # Append jump from common block to post block common_block.body.append(ir.Jump(post_label, loc=loc)) # Make if-else tree to jump to target remain_blocks = [] for remain in remainings: remain_blocks.append(max_label) max_label += 1 switch_block = post_block loc = ir.unknown_loc for i, remain in enumerate(remainings): match_expr = scope.redefine("$cp_check", loc=loc) match_rhs = scope.redefine("$cp_rhs", loc=loc) # Do comparison to match control-point variable to the exit block switch_block.body.append( ir.Assign(value=ir.Const(i, loc=loc), target=match_rhs, loc=loc), ) # Add assignment for the comparison switch_block.body.append( ir.Assign(value=ir.Expr.binop( fn=operator.eq, lhs=scope.get("$cp"), rhs=match_rhs, loc=loc, ), target=match_expr, loc=loc), ) # Insert jump to the next case [jump_target] = remain[-1].get_targets() switch_block.body.append( ir.Branch(match_expr, jump_target, remain_blocks[i], loc=loc), ) switch_block = ir.Block(scope=scope, loc=loc) blocks[remain_blocks[i]] = switch_block # Add the final jump switch_block.body.append(ir.Jump(jump_target, loc=loc)) return func_ir, common_label