add support for moving huge allocations via mremap

This leverages MREMAP_RETAIN to move huge allocations without leaving
gaps in the address space. The source is not purged since it is already
zeroed lazily if necessary.

    #include <string.h>
    #include <stdlib.h>

    int main(void) {
        for (size_t i = 0; i < 100; i++) {
            void *ptr = NULL;
            size_t old_size = 0;
            for (size_t size = 4; size < (1 << 30); size *= 2) {
                ptr = realloc(ptr, size);
                if (!ptr) return 1;
                memset(ptr + old_size, 0xff, size - old_size);
                old_size = size;
            }
            free(ptr);
        }
    }

glibc (baseline, uses mremap already): 8.412s
TCMalloc without MREMAP_RETAIN: 13.731s
jemalloc without MREMAP_RETAIN: 17.808s
jemalloc with MREMAP_RETAIN: 6.743s