code: def __call__(self, root: "ir.BaseIR") -> Dict[int, BindingSite]:
      root_frame = self.StackFrame(
          0,
          0,
          False,
          ({var: 0 for var in root.free_vars}, {}, {}),
          root,
      )
      stack = [root_frame]
      binding_sites = {}
  
      while True:
          frame = stack[-1]
          node = frame.node
          frame.child_idx += 1
          child_idx = frame.child_idx
  
          if child_idx >= len(node.children):
              # mark node as visited at potential let insertion site
              if not (node.is_effectful() or node.is_stream):
                  bind_depth = frame.bind_depth()
                  if bind_depth < frame.min_value_binding_depth:
                      if frame.scan_scope:
                          stack[bind_depth].scan_visited.add(id(node))
                      else:
                          stack[bind_depth].agg_visited.add(id(node))
                  else:
                      stack[bind_depth].visited.add(id(node))
  
              stack.pop()
  
              # if any lets being inserted here, add node to registry of
              # binding sites
              if frame.has_lifted_lets():
                  binding_sites[id(node)] = frame.make_binding_site(
                      len(stack)
                  )
  
              if not stack:
                  break
              continue
  
          child = node.children[child_idx]
  
          child_frame = frame.make_child_frame(len(stack))
  
          if isinstance(child, ir.IR):
              bind_depth = child_frame.bind_depth()
              lets = None
              if bind_depth < len(stack):
                  bind_frame = stack[bind_depth]
                  if bind_depth >= child_frame.min_value_binding_depth:
                      if id(child) in bind_frame.visited:
                          lets = bind_frame.lifted_lets
                  elif child_frame.scan_scope:
                      if id(child) in bind_frame.scan_visited:
                          lets = bind_frame.scan_lifted_lets
                  else:
                      if id(child) in bind_frame.agg_visited:
                          lets = bind_frame.agg_lifted_lets
  
              # 'lets' is either assigned before one of the 'br/has
              if lets is not None:
                  # we've seen 'child' before, should not traverse (or we will
                  # find too many lifts)
                  if id(child) not in lets:
                      # second time we've seen 'child', lift to a let
                      uid = self.uid()
                      lets[id(child)] = uid
                  continue
  
          # avoid lifting refs
          if not isinstance(child, ir.Ref):
              # first time visiting 'child'
              stack.append(child_frame)
          continue
  
      return binding_sites
  
imports: from hail import ir
  import abc
  from typing import Sequence, MutableSequence, List, Set, Dict, Optional
  from collections import namedtuple
label_desc: Use image node_id 3 for calling the CSEAnalysisPass obj's underlying member method code with example usage: obj.__call__(root) and returns: binding_sites