code: def _compute_block_attributes(function):
          
          # The attributes we use are the distance form function start, distance from function exit and whether
          # or not it has a subfunction call
          distances_from_start = FunctionDiff._distances_from_function_start(function)
          distances_from_exit = FunctionDiff._distances_from_function_exit(function)
          call_sites = function.call_sites
  
          attributes = {}
          for block in function.graph.nodes():
              if block in call_sites:
                  number_of_subfunction_calls = len(call_sites[block])
              else:
                  number_of_subfunction_calls = 0
              # there really shouldn't be blocks that can't be reached from the start, but there are for now
              dist_start = distances_from_start[block] if block in distances_from_start else 10000
              dist_exit = distances_from_exit[block] if block in distances_from_exit else 10000
  
              attributes[block] = (dist_start, dist_exit, number_of_subfunction_calls)
  
          return attributes
docs: :param function:    A normalized function object.
          :returns:           A dictionary of basic block addresses to tuples of attributes.