class_docstr: When a block does not modify a block input var, eliminate that block
  input var and use the corresponding var in the outer scope. Example:
  
  .. code-block::
  
     # Before loop_invariant_elimination pass.
     # Notice that ``%b.x`` is constant through while loop iterates.
     main(%a: (1, 2, fp32),
          %b: (1, 2, fp32)) {
       block0() {
         %loop:0: (1, 2, fp32), %loop:1: (1, 2, fp32) =            while_loop(loop_vars=(%a, %b))
           loop_cond(%a.x, %b.x) {
             %cond_var: (bool) = some_op(x=%a.x, y=%b.x)
           } -> (%cond_var)
           loop_body(%a.x, %b.x) {
             %add_0: (1, 2, fp32) = add(x=%a.x, y=%b.x)
           } -> (%add_0, %b.x)
       } -> (%loop:0, %loop:1)
     }
  
     # After loop_invariant_elimination pass.
     main(%a: (1, 2, fp32),
          %b: (1, 2, fp32)) {
       block0() {
         %loop:1: (1, 2, fp32) = identity(x=%b)
         %loop:0: (1, 2, fp32) =            while_loop(loop_vars=(%a))
           loop_cond(%a.x) {
             %cond_var: (bool) = some_op(x=%a.x, y=%b)
           } -> (%cond_var)
           loop_body(%a.x) {
             %add_0: (1, 2, fp32) = add(x=%a.x, y=%b)
           } -> (%add_0)
       } -> (%loop:0, %loop:1)
     }
  
  where we eliminate loop invariant ``%b.x`` from ``while_loop``, which returns 1
  instead of 2 outputs. We also preserve the return var names with identity.
code: def _detect_loop_invariants(while_op):
      block = while_op.blocks[1]  # body block
      loop_invariant_ids = (
          []
      )  # list of index in op.loop_vars, block.inputs
      for i, vx_in in enumerate(block.inputs):
          vx_out = block.outputs[i]  # first output is cond var.
          return_input_as_output = vx_in == vx_out
          # this block output is a var from outside of the block
  
          enclosing_block = while_op.enclosing_block
          while_op_id = enclosing_block.find_op_id_in_block(while_op)
          output_from_outside_of_block = (
              True
              if enclosing_block.is_var_visible_in_block(
                  vx_out, upto_op_with_id=while_op_id
              )
              else False
          )
          if return_input_as_output or output_from_outside_of_block:
              loop_invariant_ids.append(i)
  
      # TODO: All outputs that depend on only invariants are invariant. We
      # need to move computation out of while loop.
      return loop_invariant_ids
  
file_path: coremltools/coremltools/converters/mil/mil/passes/defs/cleanup/loop_invariant_elimination.py
imports: from coremltools.converters.mil.mil import Builder
  from coremltools.converters.mil.mil.passes.graph_pass import AbstractGraphPass
  from coremltools.converters.mil.mil.passes.helper import block_context_manager
  from coremltools.converters.mil.mil.passes.pass_registry import register_pass
label_desc: Use image node_id 2 for calling the loop_invariant_elimination obj's underlying member method code with example usage: obj._detect_loop_invariants(while_op) and returns: loop_invariant_ids