(self)
return simplified
Fold a call expression (statement) into other statements if the return value of the call expression (statement)
        is only used once, and the use site and the call site belongs to the same supernode.

        Example::

            s1 = func();
            s0 = s1;
            if (s0) ...

        after folding, it will be transformed to::

            s0 = func();
            if (s0) ...

        to avoid cases where func() is called more than once after simplification, another simplification pass will run
        on the structured graph to further transform it to::

            if (func()) ...
Fold a call expression (statement) into other statements if the return value of the call expression (statement)
        is only used once, and the use site and the call site belongs to the same supernode.
Fold a call expression ( statement ) into other statements if the return value of the call expression ( statement ) is only used once and the use site and the call site belongs to the same supernode .
def _fold_call_exprs(self) -> bool:
        """
        Fold a call expression (statement) into other statements if the return value of the call expression (statement)
        is only used once, and the use site and the call site belongs to the same supernode.

        Example::

            s1 = func();
            s0 = s1;
            if (s0) ...

        after folding, it will be transformed to::

            s0 = func();
            if (s0) ...

        to avoid cases where func() is called more than once after simplification, another simplification pass will run
        on the structured graph to further transform it to::

            if (func()) ...
        """

        simplified = False

        prop = self._compute_propagation()
        if not prop.equivalence:
            return simplified

        addr_and_idx_to_block: Dict[Tuple[int,int], Block] = { }
        for block in self.func_graph.nodes():
            addr_and_idx_to_block[(block.addr, block.idx)] = block

        for eq in prop.equivalence:
            eq: Equivalence

            # register variable == Call
            if isinstance(eq.atom0, Register):
                if isinstance(eq.atom1, Call):
                    # register variable = Call
                    call = eq.atom1
                else:
                    continue

                # find the definition of this register
                rd = self._compute_reaching_definitions()
                defs = [ d for d in rd.all_definitions
                         if d.codeloc == eq.codeloc
                         and isinstance(d.atom, atoms.Register)
                         and d.atom.reg_offset == eq.atom0.reg_offset
                         ]
                if not defs or len(defs) > 1:
                    continue
                the_def: Definition = defs[0]

                # find all uses of this definition
                all_uses: Set[CodeLocation] = set(rd.all_uses.get_uses(the_def))

                if len(all_uses) != 1:
                    continue
                u = next(iter(all_uses))

                # check the statement and make sure it's not a conditional jump
                the_block = addr_and_idx_to_block[(u.block_addr, u.block_idx)]
                if isinstance(the_block.statements[u.stmt_idx], ConditionalJump):
                    continue

                # check if the use and the definition is within the same supernode
                super_node_blocks = self._get_super_node_blocks(
                    addr_and_idx_to_block[(the_def.codeloc.block_addr, the_def.codeloc.block_idx)]
                )
                if u.block_addr not in set(b.addr for b in super_node_blocks):
                    continue

                # replace all uses
                old_block = addr_and_idx_to_block.get((u.block_addr, u.block_idx), None)
                if old_block is None:
                    continue

                # if there is an updated block, use that
                the_block = self.blocks.get(old_block, old_block)
                stmt: Statement = the_block.statements[u.stmt_idx]

                if isinstance(eq.atom0, Register):
                    src = eq.atom0
                    dst = call
                else:
                    continue

                replaced, new_block = self._replace_expr_and_update_block(the_block, u.stmt_idx, stmt, the_def, u, src,
                                                                          dst)

                if replaced:
                    self.blocks[old_block] = new_block
                    # this call has been folded to the use site. we can remove this call.
                    self._calls_to_remove.add(eq.codeloc)
                    simplified = True

        # no need to clear the cache at the end of this method
        return simplified
def _fold_call_exprs ( self ) -> bool : simplified = False prop = self . _compute_propagation ( ) if not prop . equivalence : return simplified addr_and_idx_to_block : Dict [ Tuple [ int , int ] , Block ] = { } for block in self . func_graph . nodes ( ) : addr_and_idx_to_block [ ( block . addr , block . idx ) ] = block for eq in prop . equivalence : eq : Equivalence # register variable == Call if isinstance ( eq . atom0 , Register ) : if isinstance ( eq . atom1 , Call ) : # register variable = Call call = eq . atom1 else : continue # find the definition of this register rd = self . _compute_reaching_definitions ( ) defs = [ d for d in rd . all_definitions if d . codeloc == eq . codeloc and isinstance ( d . atom , atoms . Register ) and d . atom . reg_offset == eq . atom0 . reg_offset ] if not defs or len ( defs ) > 1 : continue the_def : Definition = defs [ 0 ] # find all uses of this definition all_uses : Set [ CodeLocation ] = set ( rd . all_uses . get_uses ( the_def ) ) if len ( all_uses ) != 1 : continue u = next ( iter ( all_uses ) ) # check the statement and make sure it's not a conditional jump the_block = addr_and_idx_to_block [ ( u . block_addr , u . block_idx ) ] if isinstance ( the_block . statements [ u . stmt_idx ] , ConditionalJump ) : continue # check if the use and the definition is within the same supernode super_node_blocks = self . _get_super_node_blocks ( addr_and_idx_to_block [ ( the_def . codeloc . block_addr , the_def . codeloc . block_idx ) ] ) if u . block_addr not in set ( b . addr for b in super_node_blocks ) : continue # replace all uses old_block = addr_and_idx_to_block . get ( ( u . block_addr , u . block_idx ) , None ) if old_block is None : continue # if there is an updated block, use that the_block = self . blocks . get ( old_block , old_block ) stmt : Statement = the_block . statements [ u . stmt_idx ] if isinstance ( eq . atom0 , Register ) : src = eq . atom0 dst = call else : continue replaced , new_block = self . _replace_expr_and_update_block ( the_block , u . stmt_idx , stmt , the_def , u , src , dst ) if replaced : self . blocks [ old_block ] = new_block # this call has been folded to the use site. we can remove this call. self . _calls_to_remove . add ( eq . codeloc ) simplified = True # no need to clear the cache at the end of this method return simplified