<commit_before>/* -----------------------------------------------------------------------------
 *
 * (c) The University of Glasgow 2006-2007
 *
 * OS-specific memory management
 *
 * ---------------------------------------------------------------------------*/

// This is non-posix compliant.
// #include "PosixSource.h"

#include "Rts.h"

#include "RtsUtils.h"
#include "sm/OSMem.h"
#include "sm/HeapAlloc.h"

#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#if defined(HAVE_SYS_TYPES_H)
#include <sys/types.h>
#endif
#if defined(HAVE_SYS_MMAN_H)
#include <sys/mman.h>
#endif
#if defined(HAVE_STRING_H)
#include <string.h>
#endif
#if defined(HAVE_FCNTL_H)
#include <fcntl.h>
#endif
#if defined(HAVE_NUMA_H)
#include <numa.h>
#endif
#if defined(HAVE_NUMAIF_H)
#include <numaif.h>
#endif
#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_SYS_TIME_H)
#include <sys/time.h>
#include <sys/resource.h>
#endif

#include <errno.h>

#if defined(darwin_HOST_OS) || defined(ios_HOST_OS)
#include <mach/mach.h>
#include <mach/vm_map.h>
#include <sys/sysctl.h>
#endif

#ifndef MAP_FAILED
# define MAP_FAILED ((void *)-1)
#endif

#if defined(hpux_HOST_OS)
# ifndef MAP_ANON
#  define MAP_ANON MAP_ANONYMOUS
# endif
#endif

#ifndef darwin_HOST_OS
# undef RESERVE_FLAGS
# if defined(MAP_GUARD)
#  define RESERVE_FLAGS  MAP_GUARD /* FreeBSD */
# elif defined(MAP_NORESERVE)
#  define RESERVE_FLAGS  MAP_NORESERVE | MAP_ANON | MAP_PRIVATE;
# else
#  if defined(USE_LARGE_ADDRESS_SPACE)
#   error USE_LARGE_ADDRESS_SPACE needs MAP_NORESERVE or MAP_GUARD
#  endif
# endif
#endif

static void *next_request = 0;

void osMemInit(void)
{
    next_request = (void *)RtsFlags.GcFlags.heapBase;
}

/* -----------------------------------------------------------------------------
   The mmap() method

   On Unix-like systems, we use mmap() to allocate our memory.  We
   want memory in chunks of MBLOCK_SIZE, and aligned on an MBLOCK_SIZE
   boundary.  The mmap() interface doesn't give us this level of
   control, so we have to use some heuristics.

   In the general case, if we want a block of n megablocks, then we
   allocate n+1 and trim off the slop from either side (using
   munmap()) to get an aligned chunk of size n.  However, the next
   time we'll try to allocate directly after the previously allocated
   chunk, on the grounds that this is aligned and likely to be free.
   If it turns out that we were wrong, we have to munmap() and try
   again using the general method.

   Note on posix_memalign(): this interface is available on recent
   systems and appears to provide exactly what we want.  However, it
   turns out not to be as good as our mmap() implementation, because
   it wastes extra space (using double the address space, in a test on
   x86_64/Linux).  The problem seems to be that posix_memalign()
   returns memory that can be free()'d, so the library must store
   extra information along with the allocated block, thus messing up
   the alignment.  Hence, we don't use posix_memalign() for now.

   -------------------------------------------------------------------------- */

/*
 A wrapper around mmap(), to abstract away from OS differences in
 the mmap() interface.

 It supports the following operations:
 - reserve: find a new chunk of available address space, and make it so
            that we own it (no other library will get it), but don't actually
            allocate memory for it
            the addr is a hint for where to place the memory (and most
            of the time the OS happily ignores!)
 - commit: given a chunk of address space that we know we own, make sure
           there is some memory backing it
           the addr is not a hint, it must point into previously reserved
           address space, or bad things happen
 - reserve&commit: do both at the same time

 The naming is chosen from the Win32 API (VirtualAlloc) which does the
 same thing and has done so forever, while support for this in Unix systems
 has only been added recently and is hidden in the posix portability mess.
 The Linux manpage suggests that mmap must be passed MAP_NORESERVE in order
 to get reservation-only behavior. It is confusing because to get the reserve
 behavior we need MAP_NORESERVE (which tells the kernel not to allocate backing
 space), but heh...
*/
enum
{
    MEM_RESERVE = 1,
    MEM_COMMIT = 2,
    MEM_RESERVE_AND_COMMIT = MEM_RESERVE | MEM_COMMIT
};

#if defined(linux_HOST_OS)
static void *
linux_retry_mmap(int operation, W_ size, void *ret, void *addr, int prot, int flags)
{
    if (addr != 0 && (operation & MEM_RESERVE)) {
        // Try again with no hint address.
        // It's not clear that this can ever actually help,
        // but since our alternative is to abort, we may as well try.
        ret = mmap(0, size, prot, flags, -1, 0);
    }
    if (ret == MAP_FAILED && errno == EPERM) {
        // Linux is not willing to give us any mapping,
        // so treat this as an out-of-memory condition
        // (really out of virtual address space).
        errno = ENOMEM;
    }
    return ret;
}
#endif /* defined(linux_HOST_OS) */

static void
post_mmap_madvise(int operation, W_ size, void *ret)
{
#if defined(MADV_WILLNEED)
    if (operation & MEM_COMMIT) {
        madvise(ret, size, MADV_WILLNEED);
# if defined(MADV_DODUMP)
        madvise(ret, size, MADV_DODUMP);
# endif
    } else {
        madvise(ret, size, MADV_DONTNEED);
# if defined(MADV_DONTDUMP)
        madvise(ret, size, MADV_DONTDUMP);
# endif
    }
#endif
}

/* Returns NULL on failure; errno set */
static void *
my_mmap (void *addr, W_ size, int operation)
{
    void *ret;

#if defined(darwin_HOST_OS)
    // Without MAP_FIXED, Apple's mmap ignores addr.
    // With MAP_FIXED, it overwrites already mapped regions, whic
    // mmap(0, ... MAP_FIXED ...) is worst of all: It unmaps the program text
    // and replaces it with zeroes, causing instant death.
    // This behaviour seems to be conformant with IEEE Std 1003.1-2001.
    // Let's just use the underlying Mach Microkernel calls directly,
    // they're much nicer.

    kern_return_t err = 0;
    ret = addr;

    if(operation & MEM_RESERVE)
    {
        if(addr)    // try to allocate at address
            err = vm_allocate(mach_task_self(),(vm_address_t*) &ret,
                              size, false);
        if(!addr || err)    // try to allocate anywhere
            err = vm_allocate(mach_task_self(),(vm_address_t*) &ret,
                              size, true);
    }

    if(err) {
        // don't know what the error codes mean exactly, assume it's
        // not our problem though.
        errorBelch("memory allocation failed (requested %" FMT_Word " bytes)",
                   size);
        stg_exit(EXIT_FAILURE);
    }

    if(operation & MEM_COMMIT) {
        vm_protect(mach_task_self(), (vm_address_t)ret, size, false,
                   VM_PROT_READ|VM_PROT_WRITE);
    }

#else /* defined(darwin_HOST_OS) */

    int prot, flags;
    if (operation & MEM_COMMIT) {
        prot = PROT_READ | PROT_WRITE;
    } else {
        prot = PROT_NONE;
    }

    if (operation == MEM_RESERVE) {
# if defined(RESERVE_FLAGS)
        flags = RESERVE_FLAGS;
# else
        errorBelch("my_mmap(,,MEM_RESERVE) not supported on this platform");
# endif
    } else if (operation == MEM_COMMIT) {
        flags = MAP_FIXED | MAP_ANON | MAP_PRIVATE;
    } else {
        flags = MAP_ANON | MAP_PRIVATE;
    }

    ret = mmap(addr, size, prot, flags, -1, 0);
# if defined(linux_HOST_OS)
    if (ret == MAP_FAILED && errno == EPERM) {
        // Linux may return EPERM if it tried to give us
        // a chunk of address space below mmap_min_addr,
        // See Trac #7500.
        ret = linux_retry_mmap(operation, size, ret, addr, prot, flags);
    }
# endif
    if (ret == MAP_FAILED) {
        return NULL;
    }
    // Map in committed pages rather than take a fault for each chunk.
    // Also arrange to include them in core-dump files.
    post_mmap_madvise(operation, size, ret);
#endif /* defined(darwin_HOST_OS) */

    return ret;
}

/* Variant of my_mmap which aborts in the case of an error */
static void *
my_mmap_or_barf (void *addr, W_ size, int operation)
{
    void *ret = my_mmap(addr, size, operation);

    if (ret == NULL) {
        if (errno == ENOMEM ||
            (errno == EINVAL && sizeof(void*)==4 && size >= 0xc0000000)) {
            // If we request more than 3Gig, then we get EINVAL
            // instead of ENOMEM (at least on Linux).
            errorBelch("out of memory (requested %" FMT_Word " bytes)", size);<commit_msg>rts: Fix unused function<commit_after>/* -----------------------------------------------------------------------------
 *
 * (c) The University of Glasgow 2006-2007
 *
 * OS-specific memory management
 *
 * ---------------------------------------------------------------------------*/

// This is non-posix compliant.
// #include "PosixSource.h"

#include "Rts.h"

#include "RtsUtils.h"
#include "sm/OSMem.h"
#include "sm/HeapAlloc.h"

#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#if defined(HAVE_SYS_TYPES_H)
#include <sys/types.h>
#endif
#if defined(HAVE_SYS_MMAN_H)
#include <sys/mman.h>
#endif
#if defined(HAVE_STRING_H)
#include <string.h>
#endif
#if defined(HAVE_FCNTL_H)
#include <fcntl.h>
#endif
#if defined(HAVE_NUMA_H)
#include <numa.h>
#endif
#if defined(HAVE_NUMAIF_H)
#include <numaif.h>
#endif
#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_SYS_TIME_H)
#include <sys/time.h>
#include <sys/resource.h>
#endif

#include <errno.h>

#if defined(darwin_HOST_OS) || defined(ios_HOST_OS)
#include <mach/mach.h>
#include <mach/vm_map.h>
#include <sys/sysctl.h>
#endif

#ifndef MAP_FAILED
# define MAP_FAILED ((void *)-1)
#endif

#if defined(hpux_HOST_OS)
# ifndef MAP_ANON
#  define MAP_ANON MAP_ANONYMOUS
# endif
#endif

#ifndef darwin_HOST_OS
# undef RESERVE_FLAGS
# if defined(MAP_GUARD)
#  define RESERVE_FLAGS  MAP_GUARD /* FreeBSD */
# elif defined(MAP_NORESERVE)
#  define RESERVE_FLAGS  MAP_NORESERVE | MAP_ANON | MAP_PRIVATE;
# else
#  if defined(USE_LARGE_ADDRESS_SPACE)
#   error USE_LARGE_ADDRESS_SPACE needs MAP_NORESERVE or MAP_GUARD
#  endif
# endif
#endif

static void *next_request = 0;

void osMemInit(void)
{
    next_request = (void *)RtsFlags.GcFlags.heapBase;
}

/* -----------------------------------------------------------------------------
   The mmap() method

   On Unix-like systems, we use mmap() to allocate our memory.  We
   want memory in chunks of MBLOCK_SIZE, and aligned on an MBLOCK_SIZE
   boundary.  The mmap() interface doesn't give us this level of
   control, so we have to use some heuristics.

   In the general case, if we want a block of n megablocks, then we
   allocate n+1 and trim off the slop from either side (using
   munmap()) to get an aligned chunk of size n.  However, the next
   time we'll try to allocate directly after the previously allocated
   chunk, on the grounds that this is aligned and likely to be free.
   If it turns out that we were wrong, we have to munmap() and try
   again using the general method.

   Note on posix_memalign(): this interface is available on recent
   systems and appears to provide exactly what we want.  However, it
   turns out not to be as good as our mmap() implementation, because
   it wastes extra space (using double the address space, in a test on
   x86_64/Linux).  The problem seems to be that posix_memalign()
   returns memory that can be free()'d, so the library must store
   extra information along with the allocated block, thus messing up
   the alignment.  Hence, we don't use posix_memalign() for now.

   -------------------------------------------------------------------------- */

/*
 A wrapper around mmap(), to abstract away from OS differences in
 the mmap() interface.

 It supports the following operations:
 - reserve: find a new chunk of available address space, and make it so
            that we own it (no other library will get it), but don't actually
            allocate memory for it
            the addr is a hint for where to place the memory (and most
            of the time the OS happily ignores!)
 - commit: given a chunk of address space that we know we own, make sure
           there is some memory backing it
           the addr is not a hint, it must point into previously reserved
           address space, or bad things happen
 - reserve&commit: do both at the same time

 The naming is chosen from the Win32 API (VirtualAlloc) which does the
 same thing and has done so forever, while support for this in Unix systems
 has only been added recently and is hidden in the posix portability mess.
 The Linux manpage suggests that mmap must be passed MAP_NORESERVE in order
 to get reservation-only behavior. It is confusing because to get the reserve
 behavior we need MAP_NORESERVE (which tells the kernel not to allocate backing
 space), but heh...
*/
enum
{
    MEM_RESERVE = 1,
    MEM_COMMIT = 2,
    MEM_RESERVE_AND_COMMIT = MEM_RESERVE | MEM_COMMIT
};

#if defined(linux_HOST_OS)
static void *
linux_retry_mmap(int operation, W_ size, void *ret, void *addr, int prot, int flags)
{
    if (addr != 0 && (operation & MEM_RESERVE)) {
        // Try again with no hint address.
        // It's not clear that this can ever actually help,
        // but since our alternative is to abort, we may as well try.
        ret = mmap(0, size, prot, flags, -1, 0);
    }
    if (ret == MAP_FAILED && errno == EPERM) {
        // Linux is not willing to give us any mapping,
        // so treat this as an out-of-memory condition
        // (really out of virtual address space).
        errno = ENOMEM;
    }
    return ret;
}
#endif /* defined(linux_HOST_OS) */

static void
post_mmap_madvise(int operation, W_ size, void *ret)
{
#if defined(MADV_WILLNEED)
    if (operation & MEM_COMMIT) {
        madvise(ret, size, MADV_WILLNEED);
# if defined(MADV_DODUMP)
        madvise(ret, size, MADV_DODUMP);
# endif
    } else {
        madvise(ret, size, MADV_DONTNEED);
# if defined(MADV_DONTDUMP)
        madvise(ret, size, MADV_DONTDUMP);
# endif
    }
#endif
}

/* Returns NULL on failure; errno set */
static void *
my_mmap (void *addr, W_ size, int operation)
{
    void *ret;

#if defined(darwin_HOST_OS)
    // Without MAP_FIXED, Apple's mmap ignores addr.
    // With MAP_FIXED, it overwrites already mapped regions, whic
    // mmap(0, ... MAP_FIXED ...) is worst of all: It unmaps the program text
    // and replaces it with zeroes, causing instant death.
    // This behaviour seems to be conformant with IEEE Std 1003.1-2001.
    // Let's just use the underlying Mach Microkernel calls directly,
    // they're much nicer.

    kern_return_t err = 0;
    ret = addr;

    if(operation & MEM_RESERVE)
    {
        if(addr)    // try to allocate at address
            err = vm_allocate(mach_task_self(),(vm_address_t*) &ret,
                              size, false);
        if(!addr || err)    // try to allocate anywhere
            err = vm_allocate(mach_task_self(),(vm_address_t*) &ret,
                              size, true);
    }

    if(err) {
        // don't know what the error codes mean exactly, assume it's
        // not our problem though.
        errorBelch("memory allocation failed (requested %" FMT_Word " bytes)",
                   size);
        stg_exit(EXIT_FAILURE);
    }

    if(operation & MEM_COMMIT) {
        vm_protect(mach_task_self(), (vm_address_t)ret, size, false,
                   VM_PROT_READ|VM_PROT_WRITE);
    }

#else /* defined(darwin_HOST_OS) */

    int prot, flags;
    if (operation & MEM_COMMIT) {
        prot = PROT_READ | PROT_WRITE;
    } else {
        prot = PROT_NONE;
    }

    if (operation == MEM_RESERVE) {
# if defined(RESERVE_FLAGS)
        flags = RESERVE_FLAGS;
# else
        errorBelch("my_mmap(,,MEM_RESERVE) not supported on this platform");
# endif
    } else if (operation == MEM_COMMIT) {
        flags = MAP_FIXED | MAP_ANON | MAP_PRIVATE;
    } else {
        flags = MAP_ANON | MAP_PRIVATE;
    }

    ret = mmap(addr, size, prot, flags, -1, 0);
# if defined(linux_HOST_OS)
    if (ret == MAP_FAILED && errno == EPERM) {
        // Linux may return EPERM if it tried to give us
        // a chunk of address space below mmap_min_addr,
        // See Trac #7500.
        ret = linux_retry_mmap(operation, size, ret, addr, prot, flags);
    }
# endif
    if (ret == MAP_FAILED) {
        return NULL;
    }
#endif /* defined(darwin_HOST_OS) */

    // Map in committed pages rather than take a fault for each chunk.
    // Also arrange to include them in core-dump files.
    post_mmap_madvise(operation, size, ret);

    return ret;
}

/* Variant of my_mmap which aborts in the case of an error */
static void *
my_mmap_or_barf (void *addr, W_ size, int operation)
{
    void *ret = my_mmap(addr, size, operation);

    if (ret == NULL) {
        if (errno == ENOMEM ||
            (errno == EINVAL && sizeof(void*)==4 && size >= 0xc0000000)) {
            // If we request more than 3Gig, then we get EINVAL
            // instead of ENOMEM (at least on Linux).
            errorBelch("out of memory (requested %" FMT_Word " bytes)", size);
{'fraction_non_alphanumeric': 0.12550960978450787, 'fraction_numerical': 0.005824111822947, 'mean_word_length': 2.5324007405883564, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6154744', 'n_tokens_mistral': 5635, 'n_tokens_neox': 5361, 'n_words': 1936}