SNMALLOC_FAST_PATH void dealloc()

in src/mem/localalloc.h [584:674]


    SNMALLOC_FAST_PATH void dealloc(void* p_raw)
    {
#ifdef SNMALLOC_PASS_THROUGH
      external_alloc::free(p_raw);
#else
      // Care is needed so that dealloc(nullptr) works before init
      //  The backend allocator must ensure that a minimal page map exists
      //  before init, that maps null to a remote_deallocator that will never
      //  be in thread local state.

#  ifdef __CHERI_PURE_CAPABILITY__
      /*
       * On CHERI platforms, snap the provided pointer to its base, ignoring
       * any client-provided offset, which may have taken the pointer out of
       * bounds and so appear to designate a different object.  The base is
       * is guaranteed by monotonicity either...
       *  * to be within the bounds originally returned by alloc(), or
       *  * one past the end (in which case, the capability length must be 0).
       *
       * Setting the offset does not trap on untagged capabilities, so the tag
       * might be clear after this, as well.
       *
       * For a well-behaved client, this is a no-op: the base is already at the
       * start of the allocation and so the offset is zero.
       */
      p_raw = __builtin_cheri_offset_set(p_raw, 0);
#  endif

      capptr::AllocWild<void> p_wild = capptr_from_client(p_raw);

      /*
       * p_tame may be nullptr, even if p_raw/p_wild are not, in the case
       * where domestication fails.  We exclusively use p_tame below so that
       * such failures become no ops; in the nullptr path, which should be
       * well off the fast path, we could be slightly more aggressive and test
       * that p_raw is also nullptr and Pal::error() if not. (TODO)
       *
       * We do not rely on the bounds-checking ability of domestication here,
       * and just check the address (and, on other architectures, perhaps
       * well-formedness) of this pointer.  The remainder of the logic will
       * deal with the object's extent.
       */
      capptr::Alloc<void> p_tame = capptr_domesticate<SharedStateHandle>(
        core_alloc->backend_state_ptr(), p_wild);

      const MetaEntry& entry = SharedStateHandle::Pagemap::get_metaentry(
        core_alloc->backend_state_ptr(), address_cast(p_tame));
      if (SNMALLOC_LIKELY(local_cache.remote_allocator == entry.get_remote()))
      {
#  if defined(__CHERI_PURE_CAPABILITY__) && defined(SNMALLOC_CHECK_CLIENT)
        dealloc_cheri_checks(p_tame.unsafe_ptr());
#  endif
        if (SNMALLOC_LIKELY(CoreAlloc::dealloc_local_object_fast(
              entry, p_tame, local_cache.entropy)))
          return;
        core_alloc->dealloc_local_object_slow(entry);
        return;
      }

      if (SNMALLOC_LIKELY(entry.get_remote() != nullptr))
      {
#  if defined(__CHERI_PURE_CAPABILITY__) && defined(SNMALLOC_CHECK_CLIENT)
        dealloc_cheri_checks(p_tame.unsafe_ptr());
#  endif
        // Check if we have space for the remote deallocation
        if (local_cache.remote_dealloc_cache.reserve_space(entry))
        {
          local_cache.remote_dealloc_cache.template dealloc<sizeof(CoreAlloc)>(
            entry.get_remote()->trunc_id(), p_tame, key_global);
#  ifdef SNMALLOC_TRACING
          std::cout << "Remote dealloc fast" << p_raw << " size "
                    << alloc_size(p_raw) << std::endl;
#  endif
          return;
        }

        dealloc_remote_slow(p_tame);
        return;
      }

      // If p_tame is not null, then dealloc has been call on something
      // it shouldn't be called on.
      // TODO: Should this be tested even in the !CHECK_CLIENT case?
      snmalloc_check_client(p_tame == nullptr, "Not allocated by snmalloc.");

#  ifdef SNMALLOC_TRACING
      std::cout << "nullptr deallocation" << std::endl;
#  endif
      return;
#endif
    }