docs: def blocks_probably_identical(self, block_a, block_b, check_constants=False):
          """
          :param block_a:         The first block address.
          :param block_b:         The second block address.
          :param check_constants: Whether or not to require matching constants in blocks.
          :returns:               Whether or not the blocks appear to be identical.
          """
          # handle sim procedure blocks
          if self._project_a.is_hooked(block_a) and self._project_b.is_hooked(block_b):
              return self._project_a._sim_procedures[block_a] == self._project_b._sim_procedures[block_b]
  
          try:
              block_a = NormalizedBlock(block_a, self._function_a)
          except (SimMemoryError, SimEngineError):
              block_a = None
  
          try:
              block_b = NormalizedBlock(block_b, self._function_b)
          except (SimMemoryError, SimEngineError):
              block_b = None
  
          # if both were None then they are assumed to be the same, if only one was None they are assumed to differ
          if block_a is None and block_b is None:
              return True
          elif block_a is None or block_b is None:
              return False
  
          # if they represent a different number of blocks they are not the same
          if len(block_a.blocks) != len(block_b.blocks):
              return False
  
          # check differing constants
          try:
              diff_constants = FunctionDiff._block_diff_constants(block_a, block_b)
          except UnmatchedStatementsException:
              return False
  
          if not check_constants:
              return True
  
          # get values of differences that probably indicate no change
          acceptable_differences = self._get_acceptable_constant_differences(block_a, block_b)
  
          # todo match globals
          for c in diff_constants:
              if (c.value_a, c.value_b) in self._block_matches:
                  # constants point to matched basic blocks
                  continue
              if self._bindiff is not None and (c.value_a and c.value_b) in self._bindiff.function_matches:
                  # constants point to matched functions
                  continue
              # if both are in the binary we'll assume it's okay, although we should really match globals
              # TODO use global matches
              if self._project_a.loader.main_object.contains_addr(c.value_a) and \
                      self._project_b.loader.main_object.contains_addr(c.value_b):
                  continue
              # if the difference is equal to the difference in block addr's or successor addr's we'll say it's also okay
              if c.value_b - c.value_a in acceptable_differences:
                  continue
              # otherwise they probably are different
              return False
  
          # the blocks appear to be identical
          return True