code: def _expr2graph_impl(
      expr, target_ops, node_dict, node_list, tvm_target
  ):
      """Implementation to convert relay expr to graph data structure"""
  
      def _traverse_expr(node):
          if node in node_dict:
              return
          node_index = len(node_list)
          node_entry = {
              "node": node,
              "inputs": [],
              "types": [],
              "op": None,
              "name": None,
          }
  
          if isinstance(node, Call):
              op = node.op
              node_entry["op"] = node.op
              for arg in node.args:
                  in_node_idx = node_dict[arg]
                  if isinstance(arg, (Tuple, TupleGetItem)):
                      node_entry["inputs"] += node_list[in_node_idx][
                          "inputs"
                      ]
                  else:
                      node_entry["inputs"].append([in_node_idx, 0, 0])
              infer_out = _infer_type(node)
              out_type = infer_out._checked_type_
              if isinstance(out_type, TensorType):
                  node_entry["types"].append(out_type)
              elif isinstance(out_type, TupleType):
                  for tupe_type in out_type.fields:
                      node_entry["types"].append(tupe_type)
              else:
                  raise RuntimeError(
                      f"Unsupported output type {type(out_type)} in operator {op.name}"
                  )
  
              # Utilize tracing target to fetch workload with topo-order.
              # Since we only need workload, dummy target can be used to
              # create task.
              if op in target_ops:
                  params = []
                  for i, input_idx in enumerate(node_entry["inputs"]):
                      input_node_entry = node_list[input_idx[0]]
                      input_type = input_node_entry["types"][
                          input_idx[1]
                      ]
                      if not isinstance(
                          input_node_entry["node"],
                          (Var, Constant, Call),
                      ):
                          raise RuntimeError(
                              "Graph tuner can only tune target "
                              "operators with input node of type "
                              "relay.expr.Var/Constant/Call. Now "
                              "find a target op %s with input type %s"
                              % (
                                  op,
                                  str(type(input_node_entry["node"])),
                              )
                          )
                      free_var = relay.Var(f"var_{i}", input_type)
                      params.append(free_var)
                  call = relay.Call(node.op, params, node.attrs)
                  mod = tvm.IRModule.from_expr(
                      relay.Function(params, call)
                  )
                  relay.backend.te_compiler.get().clear()
                  tracing_target = _replace_device_with_tracing(
                      tvm_target
                  )
                  build_thread = threading.Thread(
                      target=relay.build, args=(mod, tracing_target)
                  )
                  build_thread.start()
                  build_thread.join()
          elif isinstance(node, Var):
              node_entry["name"] = node.name_hint
              node_entry["types"] = [node.type_annotation]
          elif isinstance(node, Function):
              # Ignore root node since it equals to input function expression
              if node != expr:
                  _expr2graph_impl(
                      node, target_ops, node_dict, node_list, tvm_target
                  )
              return
          elif isinstance(node, TupleGetItem):
              in_node_idx = node_dict[node.tuple_value]
              node_entry["inputs"].append([in_node_idx, node.index, 0])
          elif isinstance(node, Tuple):
              for tuple_item in node:
                  in_node_idx = node_dict[tuple_item]
                  if isinstance(tuple_item, TupleGetItem):
                      node_entry["inputs"] += node_list[in_node_idx][
                          "inputs"
                      ]
                  elif isinstance(tuple_item, Tuple):
                      raise RuntimeError(
                          "Graph tuner doesn't support nested tuple."
                      )
                  else:
                      node_entry["inputs"].append([in_node_idx, 0, 0])
          elif isinstance(node, Constant):
              node_entry["name"] = "Constant_" + str(node_index)
              node_entry["types"] = [node.checked_type]
          elif isinstance(node, tvm.ir.Op):
              return
          else:
              raise RuntimeError(
                  f"Not supported relay node type in graph tuning: {type(node)}"
              )
          node_dict[node] = node_index
          node_list.append(node_entry)
  
      relay.analysis.post_order_visit(expr, _traverse_expr)
  
file_path: tvm/python/tvm/autotvm/graph_tuner/utils/traverse_graph.py
imports: import threading
  import re
  import tvm
  from tvm import relay, autotvm
  from tvm.relay import transform
  from tvm.relay.expr import Call, TupleGetItem, Var, Constant, Tuple
  from tvm.relay.function import Function
  from tvm.relay.ty import TupleType, TensorType
  from tvm.autotvm.task import TaskExtractEnv
  from .utils import has_multiple_inputs, is_boundary_node, is_skipped_node
  from .._base import OPT_OUT_OP
label_desc: Use image node_id 4 for calling a global function with example usage: _expr2graph_impl(expr, target_ops, node_dict, node_list, tvm_target) without return types