code: def _lower_function_calls_1(
      builder, graph, defined_in, live_out, function=True
  ):
      """Lowers one function body, destructively.
  
      Mutates the given `ControlFlowGraphBuilder`, inserting `Block`s
      representing the new body.  Some of these may be the same as some
      `Block`s in the input `graph`, mutated; others may be newly
      allocated.
  
      Args:
        builder: `ControlFlowGraphBuilder` constructing the answer.
        graph: The `ControlFlowGraph` to lower.
        defined_in: A Python list of `str`.  The set of variables that
          are defined on entry to this `graph`.
        live_out: A Python list of `str`.  The set of variables that are
          live on exit from this `graph`.
        function: Python `bool`.  If `True` (the default), assume this is
          a `Function` body and convert an "exit" transfer into
          `IndirectGotoOp`; otherwise leave it as (`Program`) "exit".
  
      Raises:
        ValueError: If an invalid instruction is encountered, if a live
          variable is undefined, if different paths into a `Block` cause
          different sets of variables to be defined, or if trying to lower
          function calls in a program that already has `IndirectGotoOp`
          instructions (they confuse the liveness analysis).
      """
      liveness_map = liveness.liveness_analysis(graph, set(live_out))
      defined_map = _definedness_analysis(
          graph, defined_in, liveness_map
      )
      for i in range(graph.exit_index()):
          block = graph.block(i)
          old_instructions = block.instructions
          # Resetting block.instructions here because we will build up the
          # list of new ones in place (via the `builder`).
          block.instructions = []
          builder.append_block(block)
          builder.maybe_add_pop(
              defined_map[block].defined_into_block,
              liveness_map[block].live_into_block,
          )
          for op_i, (op, defined_out, live_out) in enumerate(
              zip(
                  old_instructions,
                  defined_map[block].defined_out_instructions,
                  liveness_map[block].live_out_instructions,
              )
          ):
              if isinstance(op, inst.PrimOp):
                  for name in inst.pattern_traverse(op.vars_in):
                      if name in inst.pattern_flatten(op.vars_out):
                          # Why not?  Because the stack discipline we are trying to
                          # implement calls for popping variables as soon as they
                          # become dead.  Now, if a PrimOp writes to the same
                          # variable as it reads, the old version of that variable
                          # dies.  Where to put the PopOp?  Before the PrimOp is no
                          # good -- it still needs to be read.  After the PrimOp is
                          # no good either -- it will pop the output, not the input.
                          # Various solutions to this problem are possible, such as
                          # adding a "drop the second-top element of this stack"
                          # instruction, or orchestrating the pushes and pops
                          # directly in the interpreter, but for now the simplest
                          # thing is to just forbid this situation.
                          # Fixing this is b/118884528.
                          msg = "Cannot lower PrimOp that writes to its own input {}."
                          raise ValueError(msg.format(name))
                  builder.append_instruction(op)
                  builder.maybe_add_pop(defined_out, live_out)
              elif isinstance(op, inst.FunctionCallOp):
                  names_pushed_here = inst.pattern_flatten(op.vars_out)
                  for name in inst.pattern_traverse(op.vars_in):
                      if name in names_pushed_here:
                          # For the same reason as above.
                          # Fixing this is b/118884528.
                          msg = "Cannot lower FunctionCallOp that writes to its own input {}."
                          raise ValueError(msg.format(name))
                  # The variables that were defined on entry to this instruction (i.e.,
                  # not pushed here) but are not live out don't need to remain on their
                  # stacks when the callee is entered.
                  defined_in = defined_out.difference(names_pushed_here)
                  to_pop = defined_in.difference(live_out)
                  for new_op in _function_entry_stack_ops(op, to_pop):
                      builder.append_instruction(new_op)
                  if op_i == len(
                      old_instructions
                  ) - 1 and _optimizable_tail_call(
                      op, builder.cur_block()
                  ):
                      builder.end_block_with_tail_call(
                          op.function.graph.block(0)
                      )
                      # The check that the tail call is optimizable is equivalent to
                      # checking that the push-pop pair below would do nothing.
                  else:
                      builder.split_block(op.function.graph.block(0))
                      builder.append_instruction(
                          # These extra levels of list protect me (I hope) from the
                          # auto-unpacking in the implementation of push_op, in the case of
                          # a function returning exactly one Tensor.
                          inst.push_op(
                              [op.function.vars_out], [op.vars_out]
                          )
                      )
                      builder.append_instruction(
                          inst.PopOp(
                              inst.pattern_flatten(op.function.vars_out)
                          )
                      )
                      # The only way this would actually add a pop is if some name written
                      # by this call was a dummy variable.
                      builder.maybe_add_pop(
                          frozenset(names_pushed_here), live_out
                      )
              elif isinstance(op, (inst.PopOp)):
                  # Presumably, lowering is applied before any `PopOp`s are present.  That
                  # said, simply propagating them is sound.  (But see the `PopOp` case in
                  # `liveness_analysis`.)
                  builder.append_instruction(op)
              else:
                  raise ValueError(
                      "Invalid instruction in block {}.".format(op)
                  )
          if function:
              builder.maybe_adjust_terminator()
  
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 2 for calling a global function with example usage: _lower_function_calls_1(builder, graph, defined_in, live_out, function) without return types