Based on control dependence graph, pick all exits (statements) that lead to the target.

        :param target_node: A CFGNode instance.
        :returns:           A set of new tainted code locations.

def _handle_control_dependence(self, target_node):
        """
        Based on control dependence graph, pick all exits (statements) that lead to the target.

        :param target_node: A CFGNode instance.
        :returns:           A set of new tainted code locations.
        """

        new_taints = set()

        # Query the CDG and figure out all control flow transitions to reach this target
        cdg_guardians = self._cdg.get_guardians(target_node)
        if not cdg_guardians:
            # this block is directly reachable from the entry point
            pass

        else:
            # For each predecessor on CDG, find the correct exit to take, and continue slicing from those exits
            for predecessor in cdg_guardians:
                exits = self._find_exits(predecessor, target_node)

                for stmt_idx, target_addresses in exits.items():
                    if stmt_idx is not None:
                        # If it's an exit statement, mark it as picked
                        self._pick_statement(predecessor.addr,
                                             self._normalize_stmt_idx(predecessor.addr, stmt_idx)
                                             )
                        # If it's the default statement, we should also pick other conditional exit statements
                        if stmt_idx == DEFAULT_STATEMENT:
                            conditional_exits = self._conditional_exits(predecessor.addr)
                            for conditional_exit_stmt_id in conditional_exits:
                                cl = CodeLocation(predecessor.addr,
                                                  self._normalize_stmt_idx(predecessor.addr, conditional_exit_stmt_id)
                                                  )
                                new_taints.add(cl)

                                self._pick_statement(predecessor.addr,
                                                     self._normalize_stmt_idx(predecessor.addr, conditional_exit_stmt_id)
                                                     )

                    if target_addresses is not None:

                        if stmt_idx is not None:
                            # If it's an exit statement, we create a new tainted code location
                            cl = CodeLocation(predecessor.addr,
                                              self._normalize_stmt_idx(predecessor.addr, stmt_idx)
                                              )
                            new_taints.add(cl)

                        # Mark those exits as picked
                        for target_address in target_addresses:
                            self._pick_exit(predecessor.addr, stmt_idx, target_address)

                    # On CFG, pick default exits of all nodes between predecessor and our target node
                    # Usually this is not required if basic blocks strictly end at control flow transitions. But this is
                    # not always the case for some architectures
                    all_simple_paths = list(networkx.all_simple_paths(self._cfg.graph, predecessor, target_node, cutoff=3))

                    previous_node = None
                    for path in all_simple_paths:
                        for node in path:
                            self._pick_statement(node.addr,
                                                 self._normalize_stmt_idx(node.addr, DEFAULT_STATEMENT))
                            if previous_node is not None:
                                self._pick_exit(previous_node.addr, DEFAULT_STATEMENT, node.addr)

        return new_taints