code: def _definedness_analysis(graph, defined_in, liveness_map):
      """Computes the defined and live variables.
  
      Specifically, for each op in each `Block`, computes the set of
      variables that are both defined coming out of it, and live coming
      out of the previous instruction.  The purpose of this analysis is to
      compute where `_lower_function_calls_1` should put `PopOp`s to
      enforce the stack discipline: The difference between the set
      computed here and the set of variables that are live out of each
      instruction is exactly the set for which a `PopOp` should be added.
  
      Why compute liveness and definedness jointly, rather than separately
      and then intersect them?  Because the purpose is to compute where to
      put the `PopOp`s, so that at the point at which any defined variable
      becomes dead, there is exactly one `PopOp`.  Placing such a `PopOp`
      will cause the variable in question to cease being defined, so this
      pass removes it from the defined set in anticipation thereof.
  
      Note that the semantics assumed by this analysis is that the control
      flow graph being analyzed does not use variable stacks internally,
      but they will only be used to implement the function sequence when
      function calls are lowered.  For this reason, a variable is treated
      as not being defined (regardless of what may be on its stack) until
      a write (as from `PrimOp` or `FunctionCallOp`) to it occurs (unless
      it comes in defined, in the `defined_in` argument).
  
      Args:
        graph: The `ControlFlowGraph` on which to perform definedness
          analysis.
        defined_in: A Python list of `str`.  The set of variables that
          are defined on entry to this `graph`.
        liveness_map: Python `dict` mapping each `Block` in `graph` to a
          `LivenessInfo` tuple, as produced by `liveness_analysis`.
  
      Returns:
        defined_map: Python `dict` mapping each `Block` in `graph` to a
          `DefinedInfo` tuple.  Each of these has two fields:
          `defined_into_block` gives the `frozenset` of `str` variable
          names defined into the block, and `defined_out_instructions`
          gives a list parallel to the `Block`s instructions list, of
          variables defined out of that instruction in the block, and live
          into it.
  
      Raises:
        ValueError: If an invalid instruction is encountered, if a live
          variable is undefined, or if different paths into a `Block`
          cause different sets of variables to be defined.
      """
      defined_map = {}
      defined = frozenset(defined_in)
  
      def record_vars_defined_on_entry(block, defined):
          if block not in defined_map:
              defined_map[block] = DefinedInfo(defined, [])
          elif defined_map[block].defined_into_block != defined:
              msg = (
                  "Inconsistent defined variable set on entry into {}.\n"
                  "Had {}, getting {}."
              ).format(
                  block, defined_map[block].defined_into_block, defined
              )
              raise ValueError(msg)
  
      record_vars_defined_on_entry(graph.block(0), defined)
  
      def check_live_variables_defined(defined, live):
          for name in live:
              if name not in defined:
                  raise ValueError(
                      "Detected undefined live variable {}.".format(
                          name
                      )
                  )
  
      for i in range(graph.exit_index()):
          block = graph.block(i)
          defined = defined_map[block].defined_into_block
          check_live_variables_defined(
              defined, liveness_map[block].live_into_block
          )
          defined = defined.intersection(
              liveness_map[block].live_into_block
          )
          # Loop invariant: At this point, `defined` is the set of variables
          # that are defined and live on entry into this op.
          for op, live_out in zip(
              block.instructions,
              liveness_map[block].live_out_instructions,
          ):
              if isinstance(op, (inst.PrimOp, inst.FunctionCallOp)):
                  defined = defined.union(
                      inst.pattern_flatten(op.vars_out)
                  )
              elif isinstance(op, inst.PopOp):
                  defined = defined.difference(op.vars)
              else:
                  raise ValueError(
                      "Invalid instruction in block {}.".format(op)
                  )
              # At this point, `defined` is the set of variables that are
              # defined on exit from this op, and live on entry into this op.
              defined_map[block].defined_out_instructions.append(
                  defined
              )
              check_live_variables_defined(defined, live_out)
              defined = defined.intersection(live_out)
              # At this point, `defined` is the set of variables that are
              # defined and live on exit from this op.
          op = block.terminator
          if isinstance(op, inst.BranchOp):
              record_vars_defined_on_entry(op.true_block, defined)
              record_vars_defined_on_entry(op.false_block, defined)
          elif isinstance(op, inst.GotoOp):
              record_vars_defined_on_entry(op.block, defined)
          elif isinstance(op, inst.PushGotoOp):
              record_vars_defined_on_entry(op.goto_block, defined)
          elif isinstance(op, inst.IndirectGotoOp):
              # Check that the return set is defined
              check_live_variables_defined(
                  defined, liveness_map[None].live_into_block
              )
          else:
              raise ValueError(
                  "Invalid terminator instruction {}.".format(op)
              )
      return defined_map
  
imports: import collections
  from tensorflow_probability.python.experimental.auto_batching import instructions
  from tensorflow_probability.python.experimental.auto_batching import liveness
label_desc: Use image node_id 1 for calling a global function with example usage: _definedness_analysis(graph, defined_in, liveness_map) and returns: defined_map