/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2002-2022. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */


/*
 * Description:	Management of memory allocators.
 *
 * Author: 	Rickard Green
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif
#define ERTS_ALLOC_C__
#define ERTS_ALC_INTERNAL__
#define ERTS_WANT_MEM_MAPPERS
#include "sys.h"
#define ERL_THREADS_EMU_INTERNAL__
#include "erl_threads.h"
#include "global.h"
#include "erl_db.h"
#include "erl_binary.h"
#include "erl_bits.h"
#include "erl_mseg.h"
#include "erl_monitor_link.h"
#include "erl_hl_timer.h"
#include "erl_cpu_topology.h"
#include "erl_thr_queue.h"
#include "erl_nfunc_sched.h"
#if defined(ERTS_ALC_T_DRV_SEL_D_STATE) || defined(ERTS_ALC_T_DRV_EV_D_STATE)
#include "erl_check_io.h"
#endif
#include "erl_bif_unique.h"

#define GET_ERL_GF_ALLOC_IMPL
#include "erl_goodfit_alloc.h"
#define GET_ERL_BF_ALLOC_IMPL
#include "erl_bestfit_alloc.h"
#define GET_ERL_AF_ALLOC_IMPL
#include "erl_afit_alloc.h"
#define GET_ERL_AOFF_ALLOC_IMPL
#include "erl_ao_firstfit_alloc.h"


#if ERTS_MAX_NO_OF_SCHEDULERS > ERTS_AU_MAX_PREF_ALLOC_INSTANCES
#  error "Too many schedulers; cannot create that many pref alloc instances"
#endif

#define ERTS_ALC_DEFAULT_MAX_THR_PREF ERTS_MAX_NO_OF_SCHEDULERS

#if defined(SMALL_MEMORY) || defined(VALGRIND) || defined(ADDRESS_SANITIZER)
#define AU_ALLOC_DEFAULT_ENABLE(X)	0
#else
#define AU_ALLOC_DEFAULT_ENABLE(X)	(X)
#endif

#define ERTS_ALC_DEFAULT_ENABLED_ACUL 60
#define ERTS_ALC_DEFAULT_ENABLED_ACUL_EHEAP_ALLOC 45
#define ERTS_ALC_DEFAULT_ENABLED_ACUL_LL_ALLOC 85

#define ERTS_ALC_DEFAULT_ACUL ERTS_ALC_DEFAULT_ENABLED_ACUL
#define ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC ERTS_ALC_DEFAULT_ENABLED_ACUL_EHEAP_ALLOC
#define ERTS_ALC_DEFAULT_ACUL_LL_ALLOC ERTS_ALC_DEFAULT_ENABLED_ACUL_LL_ALLOC


#ifdef DEBUG
static Uint install_debug_functions(void);
#if 0
#define HARD_DEBUG
#ifdef __GNUC__
#warning "* * * * * * * * * * * * * *"
#warning "* HARD DEBUG IS ENABLED!  *"
#warning "* * * * * * * * * * * * * *"
#endif
#endif
#endif

static int lock_all_physical_memory = 0;

ErtsAllocatorFunctions_t ERTS_WRITE_UNLIKELY(erts_allctrs[ERTS_ALC_A_MAX+1]);
ErtsAllocatorInfo_t erts_allctrs_info[ERTS_ALC_A_MAX+1];
ErtsAllocatorThrSpec_t ERTS_WRITE_UNLIKELY(erts_allctr_thr_spec[ERTS_ALC_A_MAX+1]);

#define ERTS_MIN(A, B) ((A) < (B) ? (A) : (B))
#define ERTS_MAX(A, B) ((A) > (B) ? (A) : (B))

typedef union {
    GFAllctr_t gfa;
    char align_gfa[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(GFAllctr_t))];
    BFAllctr_t bfa;
    char align_bfa[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(BFAllctr_t))];
    AFAllctr_t afa;
    char align_afa[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(AFAllctr_t))];
    AOFFAllctr_t aoffa;
    char align_aoffa[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(AOFFAllctr_t))];
} ErtsAllocatorState_t erts_align_attribute(ERTS_CACHE_LINE_SIZE);

static ErtsAllocatorState_t std_alloc_state;
static ErtsAllocatorState_t ll_alloc_state;
static ErtsAllocatorState_t sl_alloc_state;
static ErtsAllocatorState_t temp_alloc_state;
static ErtsAllocatorState_t eheap_alloc_state;
static ErtsAllocatorState_t binary_alloc_state;
static ErtsAllocatorState_t ets_alloc_state;
static ErtsAllocatorState_t driver_alloc_state;
static ErtsAllocatorState_t fix_alloc_state;
static ErtsAllocatorState_t literal_alloc_state;
static ErtsAllocatorState_t test_alloc_state;

enum {
    ERTS_ALC_INFO_A_ALLOC_UTIL = ERTS_ALC_A_MAX + 1,
    ERTS_ALC_INFO_A_MSEG_ALLOC,
    ERTS_ALC_INFO_A_ERTS_MMAP,
    ERTS_ALC_INFO_A_END
};

typedef struct {
    erts_atomic32_t refc;
    int only_sz;
    int internal;
    Uint req_sched;
    Process *proc;
    ErtsIRefStorage iref;
    int allocs[ERTS_ALC_INFO_A_END - ERTS_ALC_A_MIN + 1];
} ErtsAllocInfoReq;

ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(aireq,
                                 ErtsAllocInfoReq,
                                 5,
                                 ERTS_ALC_T_AINFO_REQ)

ErtsAlcType_t erts_fix_core_allocator_ix;
erts_tsd_key_t erts_thr_alloc_data_key;

Uint ERTS_WRITE_UNLIKELY(erts_no_dirty_alloc_instances);

struct au_init {
    int enable;
    int thr_spec;
    int disable_allowed;
    int thr_spec_allowed;
    int carrier_migration_allowed;
    ErtsAlcStrat_t	astrat;
    struct {
	AllctrInit_t	util;
	GFAllctrInit_t	gf;
	BFAllctrInit_t	bf;
	AFAllctrInit_t	af;
	AOFFAllctrInit_t aoff;
    } init;
    struct {
	int mmbcs;
	int lmbcs;
	int smbcs;
	int mmmbc;
    } default_;
};

#define DEFAULT_ALLCTR_INIT {		\
    ERTS_DEFAULT_ALLCTR_INIT,		\
    ERTS_DEFAULT_GF_ALLCTR_INIT,	\
    ERTS_DEFAULT_BF_ALLCTR_INIT,	\
    ERTS_DEFAULT_AF_ALLCTR_INIT,	\
    ERTS_DEFAULT_AOFF_ALLCTR_INIT       \
}

typedef struct {
    int erts_alloc_config;
#if HAVE_ERTS_MSEG
    ErtsMsegInit_t mseg;
#endif
    int trim_threshold;
    int top_pad;
    int dirty_alloc_insts;
    AlcUInit_t alloc_util;
    struct au_init sl_alloc;
    struct au_init std_alloc;
    struct au_init ll_alloc;
    struct au_init temp_alloc;
    struct au_init eheap_alloc;
    struct au_init binary_alloc;
    struct au_init ets_alloc;
    struct au_init driver_alloc;
    struct au_init fix_alloc;
    struct au_init literal_alloc;
    struct au_init test_alloc;
} erts_alc_hndl_args_init_t;

#define ERTS_AU_INIT__ {0, 0, 1, 1, 1, \
                        ERTS_ALC_S_GOODFIT, DEFAULT_ALLCTR_INIT, \
                        {1,1,1,1}}

#define SET_DEFAULT_ALLOC_OPTS(IP)					\
do {									\
    struct au_init aui__ = ERTS_AU_INIT__;				\
    sys_memcpy((void *) (IP), (void *) &aui__, sizeof(struct au_init));	\
} while (0)

static void
set_default_sl_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 1;
    ip->astrat			= ERTS_ALC_S_GOODFIT;
    ip->init.util.name_prefix	= "sl_";
    ip->init.util.alloc_no	= ERTS_ALC_A_SHORT_LIVED;
    ip->init.util.cp            = ERTS_ALC_A_SHORT_LIVED;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 128*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 32*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_SHORT_LIVED;
    ip->init.util.rsbcst	= 80;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL;
}

static void
set_default_std_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 1;
    ip->astrat			= ERTS_ALC_S_BESTFIT;
    ip->init.util.name_prefix	= "std_";
    ip->init.util.alloc_no	= ERTS_ALC_A_STANDARD;
    ip->init.util.cp            = ERTS_ALC_A_STANDARD;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 128*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 32*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_STANDARD;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL;
}

static void
set_default_ll_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 0;
    ip->astrat			= ERTS_ALC_S_BESTFIT;
    ip->init.bf.ao		= 1;
    ip->init.util.ramv		= 0;
    ip->init.util.mmsbc		= 0;
    ip->init.util.sbct		= ~((UWord) 0);
    ip->init.util.name_prefix	= "ll_";
    ip->init.util.alloc_no	= ERTS_ALC_A_LONG_LIVED;
    ip->init.util.cp            = ERTS_ALC_A_LONG_LIVED;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 2*1024*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 1*1024*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_LONG_LIVED;
    ip->init.util.asbcst	= 0;
    ip->init.util.rsbcst	= 0;
    ip->init.util.rsbcmt	= 0;
    ip->init.util.rmbcmt	= 0;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL_LL_ALLOC;
}

static void
set_default_literal_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
#ifdef ADDRESS_SANITIZER
    ip->enable			= 0;
#else
    ip->enable			= 1;
#endif
    ip->thr_spec		= 0;
    ip->disable_allowed         = 0;
    ip->thr_spec_allowed        = 0;
    ip->carrier_migration_allowed = 0;
    ip->astrat			= ERTS_ALC_S_BESTFIT;
    ip->init.bf.ao		= 1;
    ip->init.util.ramv		= 0;
    ip->init.util.mmsbc		= 0;
    ip->init.util.sbct		= ~((UWord) 0);
    ip->init.util.name_prefix	= "literal_";
    ip->init.util.alloc_no	= ERTS_ALC_A_LITERAL;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 1024*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 256*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_LITERAL;
    ip->init.util.asbcst	= 0;
    ip->init.util.rsbcst	= 0;
    ip->init.util.rsbcmt	= 0;
    ip->init.util.rmbcmt	= 0;
    ip->init.util.acul		= 0;

#if defined(ARCH_32)
# if HAVE_ERTS_MSEG
    ip->init.util.mseg_alloc   = &erts_alcu_literal_32_mseg_alloc;
    ip->init.util.mseg_realloc = &erts_alcu_literal_32_mseg_realloc;
    ip->init.util.mseg_dealloc = &erts_alcu_literal_32_mseg_dealloc;
# endif
    ip->init.util.sys_alloc    = &erts_alcu_literal_32_sys_alloc;
    ip->init.util.sys_realloc  = &erts_alcu_literal_32_sys_realloc;
    ip->init.util.sys_dealloc  = &erts_alcu_literal_32_sys_dealloc;
#elif defined(ARCH_64)
# ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION
    ip->init.util.mseg_alloc    = &erts_alcu_mmapper_mseg_alloc;
    ip->init.util.mseg_realloc  = &erts_alcu_mmapper_mseg_realloc;
    ip->init.util.mseg_dealloc  = &erts_alcu_mmapper_mseg_dealloc;
    ip->init.util.mseg_mmapper  = &erts_literal_mmapper;
# endif
#else
# error Unknown architecture
#endif
}

static void
set_default_temp_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 1;
    ip->disable_allowed         = 0;
    ip->carrier_migration_allowed = 0;
    ip->astrat			= ERTS_ALC_S_AFIT;
    ip->init.util.name_prefix	= "temp_";
    ip->init.util.alloc_no	= ERTS_ALC_A_TEMPORARY;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 128*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 32*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_TEMPORARY;
    ip->init.util.rsbcst	= 90;
    ip->init.util.rmbcmt	= 100;
}

static void
set_default_eheap_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 1;
    ip->astrat			= ERTS_ALC_S_GOODFIT;
    ip->init.util.name_prefix	= "eheap_";
    ip->init.util.alloc_no	= ERTS_ALC_A_EHEAP;
    ip->init.util.cp            = ERTS_ALC_A_EHEAP;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 512*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 256*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_EHEAP;
    ip->init.util.rsbcst	= 50;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC;
}

static void
set_default_binary_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 1;
    ip->astrat			= ERTS_ALC_S_BESTFIT;
    ip->init.util.name_prefix	= "binary_";
    ip->init.util.alloc_no	= ERTS_ALC_A_BINARY;
    ip->init.util.cp            = ERTS_ALC_A_BINARY;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 128*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 32*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_BINARY;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL;
    ip->init.util.atags		= 1;
}

static void
set_default_ets_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 1;
    ip->astrat			= ERTS_ALC_S_BESTFIT;
    ip->init.util.name_prefix	= "ets_";
    ip->init.util.alloc_no	= ERTS_ALC_A_ETS;
    ip->init.util.cp            = ERTS_ALC_A_ETS;
    ip->init.util.mmbc0         = 0;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 128*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 32*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_ETS;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL;
}

static void
set_default_driver_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 1;
    ip->astrat			= ERTS_ALC_S_BESTFIT;
    ip->init.util.name_prefix	= "driver_";
    ip->init.util.alloc_no	= ERTS_ALC_A_DRIVER;
    ip->init.util.cp            = ERTS_ALC_A_DRIVER;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 128*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 32*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_DRIVER;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL;
    ip->init.util.atags		= 1;
}

static void
set_default_fix_alloc_opts(struct au_init *ip,
			   size_t *fix_type_sizes)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= AU_ALLOC_DEFAULT_ENABLE(1);
    ip->thr_spec		= 1;
    ip->astrat			= ERTS_ALC_S_BESTFIT;
    ip->init.bf.ao = 1;
    ip->init.util.name_prefix	= "fix_";
    ip->init.util.fix_type_size	= fix_type_sizes;
    ip->init.util.alloc_no	= ERTS_ALC_A_FIXED_SIZE;
    ip->init.util.cp            = ERTS_ALC_A_FIXED_SIZE;
#ifndef SMALL_MEMORY
    ip->init.util.mmbcs 	= 128*1024; /* Main carrier size */
#else
    ip->init.util.mmbcs 	= 128*1024; /* Main carrier size */
#endif
    ip->init.util.ts 		= ERTS_ALC_MTA_FIXED_SIZE;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL;
}

static void
set_default_test_alloc_opts(struct au_init *ip)
{
    SET_DEFAULT_ALLOC_OPTS(ip);
    ip->enable			= 0; /* Disabled by default */
    ip->thr_spec		= -1;
    ip->astrat			= ERTS_ALC_S_FIRSTFIT;
    ip->init.aoff.crr_order     = FF_AOFF;
    ip->init.aoff.blk_order     = FF_BF;
    ip->init.util.name_prefix	= "test_";
    ip->init.util.alloc_no	= ERTS_ALC_A_TEST;
    ip->init.util.cp            = ERTS_ALC_A_TEST;
    ip->init.util.mmbcs 	= 0; /* Main carrier size */
    ip->init.util.ts 		= ERTS_ALC_MTA_TEST;
    ip->init.util.acul		= ERTS_ALC_DEFAULT_ACUL;
    ip->init.util.atags		= 1;

    /* Use a constant minimal MBC size */
#if ERTS_SA_MB_CARRIERS
    ip->init.util.smbcs = ERTS_SACRR_UNIT_SZ;
    ip->init.util.lmbcs = ERTS_SACRR_UNIT_SZ;
    ip->init.util.sbct  = ERTS_SACRR_UNIT_SZ;
#else
    ip->init.util.smbcs = 1 << 12;
    ip->init.util.lmbcs = 1 << 12;
    ip->init.util.sbct  = 1 << 12;
#endif
}



static void
adjust_tpref(struct au_init *ip, int no_sched, int no_dirty_inst)
{
    if (ip->thr_spec) {
        ip->thr_spec = no_sched + no_dirty_inst;
	ip->thr_spec *= -1; /* thread preferred */

	/* If default ... */

	/* ... shrink main multi-block carrier size */
	if (ip->default_.mmbcs)
	    ip->init.util.mmbcs /= ERTS_MIN(4, no_sched);
	/* ... shrink largest multi-block carrier size */
	if (ip->default_.lmbcs)
	    ip->init.util.lmbcs /= ERTS_MIN(2, no_sched);
	/* ... shrink smallest multi-block carrier size */
	if (ip->default_.smbcs)
	    ip->init.util.smbcs /= ERTS_MIN(4, no_sched);
    }
}


static void handle_args(int *, char **, erts_alc_hndl_args_init_t *);

static void
set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init, int ncpu);

static void
start_au_allocator(ErtsAlcType_t alctr_n,
		   struct au_init *init,
		   ErtsAllocatorState_t *state);

static void
refuse_af_strategy(struct au_init *init)
{
    if (init->astrat == ERTS_ALC_S_AFIT)
	init->astrat = ERTS_ALC_S_GOODFIT;
}

#ifdef HARD_DEBUG
static void hdbg_init(void);
#endif

static void adjust_fix_alloc_sizes(UWord extra_block_size)
{
    
    if (extra_block_size && erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].enabled) {
	int j;

	if (erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].thr_spec) {
	    int i;
	    ErtsAllocatorThrSpec_t* tspec;

	    tspec = &erts_allctr_thr_spec[ERTS_ALC_A_FIXED_SIZE];	
	    ASSERT(tspec->enabled);

	    for (i=0; i < tspec->size; i++) {
		Allctr_t* allctr = tspec->allctr[i];
		for (j=0; j < ERTS_ALC_NO_FIXED_SIZES; ++j) {
                    size_t size = allctr->fix[j].type_size;
                    size = MAX(size + extra_block_size,
                               sizeof(ErtsAllctrDDBlock_t));
		    allctr->fix[j].type_size = size;
		}
	    }
	}
	else
	{
	    Allctr_t* allctr = erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].extra;
	    for (j=0; j < ERTS_ALC_NO_FIXED_SIZES; ++j) {
                size_t size = allctr->fix[j].type_size;
                size = MAX(size + extra_block_size,
                           sizeof(ErtsAllctrDDBlock_t));
                allctr->fix[j].type_size = size;
	    }
	}
    }
}

static ERTS_INLINE int
strategy_support_carrier_migration(struct au_init *auip)
{
    /*
     * Currently only aoff* and ageff* support carrier
     * migration, i.e, type AOFIRSTFIT.
     */
    return auip->astrat == ERTS_ALC_S_FIRSTFIT;
}

static ERTS_INLINE void
adjust_carrier_migration_support(struct au_init *auip)
{
    if (auip->init.util.acul) {
	auip->thr_spec = -1; /* Need thread preferred */

	/*
	 * If strategy cannot handle carrier migration,
	 * default to a strategy that can...
	 */
	if (!strategy_support_carrier_migration(auip)) {
	    /* Default to aoffcbf */
	    auip->astrat = ERTS_ALC_S_FIRSTFIT;
	    auip->init.aoff.crr_order = FF_AOFF;
	    auip->init.aoff.blk_order = FF_BF;
	}
    }
}

void
erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
{
    UWord extra_block_size = 0;
    int i, ncpu;
    erts_alc_hndl_args_init_t init = {
	0,
#if HAVE_ERTS_MSEG
	ERTS_MSEG_INIT_DEFAULT_INITIALIZER,
#endif
	ERTS_DEFAULT_TRIM_THRESHOLD,
	ERTS_DEFAULT_TOP_PAD,
	0, /* Default dirty alloc instances */
	ERTS_DEFAULT_ALCU_INIT,
    };
    size_t fix_type_sizes[ERTS_ALC_NO_FIXED_SIZES] = {0};

    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_PROC)]
	= sizeof(Process);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR)]
	= sizeof(ErtsMonitorDataHeap);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_LINK)]
	= sizeof(ErtsILink);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_SEL_D_STATE)]
	= sizeof(ErtsDrvSelectDataState);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NIF_SEL_D_STATE)]
        = sizeof(ErtsNifSelectDataState);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MSG_REF)]
	= sizeof(ErtsMessageRef);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_THR_Q_EL_SL)]
	= sizeof(ErtsThrQElement_t);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_LL_PTIMER)]
	= erts_timer_type_size(ERTS_ALC_T_LL_PTIMER);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_HL_PTIMER)]
	= erts_timer_type_size(ERTS_ALC_T_HL_PTIMER);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_BIF_TIMER)]
	= erts_timer_type_size(ERTS_ALC_T_BIF_TIMER);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MREF_NSCHED_ENT)]
	= sizeof(ErtsNSchedMagicRefTableEntry);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_PREF_NSCHED_ENT)]
	= sizeof(ErtsNSchedPidRefTableEntry);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MINDIRECTION)]
	= ERTS_MAGIC_BIN_UNALIGNED_SIZE(sizeof(ErtsMagicIndirectionWord));
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_RECV_MARK_BLK)]
        = sizeof(ErtsRecvMarkerBlock);
    fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_SIGQ_BUFFERS)]
        = sizeof(ErtsSignalInQueueBufferArray);

#ifdef HARD_DEBUG
    hdbg_init();
#endif

    erts_tsd_key_create(&erts_thr_alloc_data_key, "erts_alc_data_key");

    lock_all_physical_memory = 0;

    ncpu = eaiop->ncpu;
    if (ncpu < 1)
	ncpu = 1;

    erts_sys_alloc_init();
    erts_init_utils_mem();

    set_default_sl_alloc_opts(&init.sl_alloc);
    set_default_std_alloc_opts(&init.std_alloc);
    set_default_ll_alloc_opts(&init.ll_alloc);
    set_default_temp_alloc_opts(&init.temp_alloc);
    set_default_eheap_alloc_opts(&init.eheap_alloc);
    set_default_binary_alloc_opts(&init.binary_alloc);
    set_default_ets_alloc_opts(&init.ets_alloc);
    set_default_driver_alloc_opts(&init.driver_alloc);
    set_default_fix_alloc_opts(&init.fix_alloc,
			       fix_type_sizes);
    set_default_literal_alloc_opts(&init.literal_alloc);
    set_default_test_alloc_opts(&init.test_alloc);

    if (argc && argv)
	handle_args(argc, argv, &init);

    if (lock_all_physical_memory) {
#ifdef HAVE_MLOCKALL
	errno = 0;
	if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) {
	    int err = errno;
	    const char *errstr = err ? strerror(err) : "unknown";
	    erts_exit(1, "Failed to lock physical memory: %s (%d)\n",
		     errstr, err);
	}
#else
	erts_exit(1, "Failed to lock physical memory: Not supported\n");
#endif
    }

    erts_no_dirty_alloc_instances = init.dirty_alloc_insts;

    /* Make adjustments for carrier migration support */
    init.temp_alloc.init.util.acul = 0;
    adjust_carrier_migration_support(&init.sl_alloc);
    adjust_carrier_migration_support(&init.std_alloc);
    adjust_carrier_migration_support(&init.ll_alloc);
    adjust_carrier_migration_support(&init.eheap_alloc);
    adjust_carrier_migration_support(&init.binary_alloc);
    adjust_carrier_migration_support(&init.ets_alloc);
    adjust_carrier_migration_support(&init.driver_alloc);
    adjust_carrier_migration_support(&init.fix_alloc);
    adjust_carrier_migration_support(&init.literal_alloc);

    if (init.erts_alloc_config) {
	/* Adjust flags that erts_alloc_config won't like */

	/* No thread specific instances */
	init.temp_alloc.thr_spec = 0;
	init.sl_alloc.thr_spec = 0;
	init.std_alloc.thr_spec = 0;
	init.ll_alloc.thr_spec = 0;
	init.eheap_alloc.thr_spec = 0;
	init.binary_alloc.thr_spec = 0;
	init.ets_alloc.thr_spec = 0;
	init.driver_alloc.thr_spec = 0;
	init.fix_alloc.thr_spec = 0;
        init.literal_alloc.thr_spec = 0;

	/* No carrier migration */
	init.temp_alloc.init.util.acul = 0;
	init.sl_alloc.init.util.acul = 0;
	init.std_alloc.init.util.acul = 0;
	init.ll_alloc.init.util.acul = 0;
	init.eheap_alloc.init.util.acul = 0;
	init.binary_alloc.init.util.acul = 0;
	init.ets_alloc.init.util.acul = 0;
	init.driver_alloc.init.util.acul = 0;
	init.fix_alloc.init.util.acul = 0;
        init.literal_alloc.init.util.acul = 0;
    }

    /* Only temp_alloc can use thread specific interface */
    if (init.temp_alloc.thr_spec)
        init.temp_alloc.thr_spec = erts_no_schedulers + init.dirty_alloc_insts;

    /* Others must use thread preferred interface */
    adjust_tpref(&init.sl_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.std_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.ll_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.eheap_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.binary_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.ets_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.driver_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.fix_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.literal_alloc, erts_no_schedulers, init.dirty_alloc_insts);
    adjust_tpref(&init.test_alloc, erts_no_schedulers, init.dirty_alloc_insts);


    /*
     * The following allocators cannot be run with afit strategy.
     * Make sure they don't...
     */
    refuse_af_strategy(&init.sl_alloc);
    refuse_af_strategy(&init.std_alloc);
    refuse_af_strategy(&init.ll_alloc);
    refuse_af_strategy(&init.eheap_alloc);
    refuse_af_strategy(&init.binary_alloc);
    refuse_af_strategy(&init.ets_alloc);
    refuse_af_strategy(&init.driver_alloc);
    refuse_af_strategy(&init.fix_alloc);
    refuse_af_strategy(&init.literal_alloc);
    refuse_af_strategy(&init.test_alloc);

    if (!init.temp_alloc.thr_spec)
	refuse_af_strategy(&init.temp_alloc);

#if HAVE_ERTS_MSEG
    init.mseg.nos = erts_no_schedulers;
    init.mseg.ndai = init.dirty_alloc_insts;
    erts_mseg_init(&init.mseg);
#endif

    erts_alcu_init(&init.alloc_util);
    erts_afalc_init();
    erts_bfalc_init();
    erts_gfalc_init();
    erts_aoffalc_init();

    for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
	erts_allctrs[i].alloc		= NULL;
	erts_allctrs[i].realloc		= NULL;
	erts_allctrs[i].free		= NULL;
	erts_allctrs[i].extra		= NULL;
	erts_allctrs_info[i].alloc_util	= 0;
	erts_allctrs_info[i].enabled	= 0;
	erts_allctrs_info[i].thr_spec	= 0;
	erts_allctrs_info[i].extra	= NULL;
    }

    erts_allctrs[ERTS_ALC_A_SYSTEM].alloc		= erts_sys_alloc;
    erts_allctrs[ERTS_ALC_A_SYSTEM].realloc		= erts_sys_realloc;
    erts_allctrs[ERTS_ALC_A_SYSTEM].free		= erts_sys_free;
    erts_allctrs_info[ERTS_ALC_A_SYSTEM].enabled	= 1;

    set_au_allocator(ERTS_ALC_A_TEMPORARY, &init.temp_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_SHORT_LIVED, &init.sl_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_STANDARD, &init.std_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_LONG_LIVED, &init.ll_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_EHEAP, &init.eheap_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_BINARY, &init.binary_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_ETS, &init.ets_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_DRIVER, &init.driver_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_FIXED_SIZE, &init.fix_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_LITERAL, &init.literal_alloc, ncpu);
    set_au_allocator(ERTS_ALC_A_TEST, &init.test_alloc, ncpu);

    for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
	if (!erts_allctrs[i].alloc)
	    erts_exit(ERTS_ABORT_EXIT,
		     "Missing alloc function for %s\n", ERTS_ALC_A2AD(i));
	if (!erts_allctrs[i].realloc)
	    erts_exit(ERTS_ABORT_EXIT,
		     "Missing realloc function for %s\n", ERTS_ALC_A2AD(i));
	if (!erts_allctrs[i].free)
	    erts_exit(ERTS_ABORT_EXIT,
		     "Missing free function for %s\n", ERTS_ALC_A2AD(i));
    }

    sys_alloc_opt(SYS_ALLOC_OPT_TRIM_THRESHOLD, init.trim_threshold);
    sys_alloc_opt(SYS_ALLOC_OPT_TOP_PAD, init.top_pad);

    start_au_allocator(ERTS_ALC_A_TEMPORARY,
		       &init.temp_alloc,
		       &temp_alloc_state);

    start_au_allocator(ERTS_ALC_A_SHORT_LIVED,
		       &init.sl_alloc,
		       &sl_alloc_state);

    start_au_allocator(ERTS_ALC_A_STANDARD,
		       &init.std_alloc,
		       &std_alloc_state);

    start_au_allocator(ERTS_ALC_A_LONG_LIVED,
		       &init.ll_alloc,
		       &ll_alloc_state);
    start_au_allocator(ERTS_ALC_A_EHEAP,
		       &init.eheap_alloc,
		       &eheap_alloc_state);

    start_au_allocator(ERTS_ALC_A_BINARY,
		       &init.binary_alloc,
		       &binary_alloc_state);

    start_au_allocator(ERTS_ALC_A_ETS,
		       &init.ets_alloc,
		       &ets_alloc_state);

    start_au_allocator(ERTS_ALC_A_DRIVER,
		       &init.driver_alloc,
		       &driver_alloc_state);

    start_au_allocator(ERTS_ALC_A_FIXED_SIZE,
		       &init.fix_alloc,
		       &fix_alloc_state);
    start_au_allocator(ERTS_ALC_A_LITERAL,
                       &init.literal_alloc,
                       &literal_alloc_state);
    start_au_allocator(ERTS_ALC_A_TEST,
		       &init.test_alloc,
		       &test_alloc_state);

    init_aireq_alloc();

#ifdef DEBUG
    extra_block_size += install_debug_functions();
#endif
    adjust_fix_alloc_sizes(extra_block_size);
}

void
erts_alloc_late_init(void)
{

}

static void *
erts_realloc_fixed_size(ErtsAlcType_t type, void *extra, void *p, Uint size)
{
    erts_exit(ERTS_ABORT_EXIT,
	     "Attempt to reallocate a block of the fixed size type %s\n",
	     ERTS_ALC_T2TD(type));
}


static void
set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init, int ncpu)
{
    ErtsAllocatorFunctions_t *af = &erts_allctrs[alctr_n];
    ErtsAllocatorInfo_t *ai = &erts_allctrs_info[alctr_n];
    ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[alctr_n];

    /*
     * Some allocators are forced on if halfword heap is used.
     */
    if (init->init.util.force)
	init->enable = 1;

    tspec->enabled = 0;
    tspec->dd = 0;
    tspec->aix = alctr_n;
    tspec->size	= 0;
    ai->thr_spec = 0;

    if (!init->enable) {
	af->alloc = erts_sys_alloc;
	af->realloc = erts_sys_realloc;
	af->free = erts_sys_free;
	af->extra = NULL;
	ai->alloc_util = 0;
	ai->enabled = 0;
	ai->extra = NULL;
	return;
    }

    if (init->thr_spec) {
	if (init->thr_spec > 0) {
	    af->alloc = erts_alcu_alloc_thr_spec;
	    if (init->init.util.fix_type_size)
		af->realloc = erts_realloc_fixed_size;
	    else if (init->init.util.ramv)
		af->realloc = erts_alcu_realloc_mv_thr_spec;
	    else
		af->realloc = erts_alcu_realloc_thr_spec;
	    af->free = erts_alcu_free_thr_spec;
	}
	else {
	    af->alloc = erts_alcu_alloc_thr_pref;
	    if (init->init.util.fix_type_size)
		af->realloc = erts_realloc_fixed_size;
	    else if (init->init.util.ramv)
		af->realloc = erts_alcu_realloc_mv_thr_pref;
	    else
		af->realloc = erts_alcu_realloc_thr_pref;
	    af->free = erts_alcu_free_thr_pref;
	    tspec->dd = 1;
	}

	tspec->enabled	= 1;
	tspec->size	= abs(init->thr_spec) + 1;

	ai->thr_spec	= tspec->size;
    }
    else
	if (init->init.util.ts) {
	af->alloc = erts_alcu_alloc_ts;
	if (init->init.util.fix_type_size)
	    af->realloc = erts_realloc_fixed_size;
	else if (init->init.util.ramv)
	    af->realloc = erts_alcu_realloc_mv_ts;
	else
	    af->realloc = erts_alcu_realloc_ts;
	af->free = erts_alcu_free_ts;
    }
    else
    {
        erts_exit(ERTS_ABORT_EXIT, "%salloc is not thread safe\n",
                 init->init.util.name_prefix);
    }
    af->extra	= NULL;
    ai->alloc_util	= 1;
    ai->enabled		= 1;
}

static void
start_au_allocator(ErtsAlcType_t alctr_n,
		   struct au_init *init,
		   ErtsAllocatorState_t *state)
{
    int i;
    int size = 1;
    void *as0;
    ErtsAlcStrat_t astrat;
    ErtsAllocatorFunctions_t *af = &erts_allctrs[alctr_n];
    ErtsAllocatorInfo_t *ai = &erts_allctrs_info[alctr_n];
    ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[alctr_n];
    ErtsAlcFixList_t *fix_lists = NULL;
    size_t fix_list_size = 0;

    if (!init->enable)
	return;

    if (init->thr_spec) {
	char *states = erts_sys_alloc(0,
				      NULL,
				      ((sizeof(Allctr_t *)
					* (tspec->size + 1))
				       + (sizeof(ErtsAllocatorState_t)
					  * tspec->size)
				       + ERTS_CACHE_LINE_SIZE - 1));
	if (!states)
	    erts_exit(ERTS_ABORT_EXIT,
		     "Failed to allocate allocator states for %salloc\n",
		     init->init.util.name_prefix);
	tspec->allctr = (Allctr_t **) states;
	states += sizeof(Allctr_t *) * (tspec->size + 1);
	states = ((((UWord) states) & ERTS_CACHE_LINE_MASK)
		  ? (char *) ((((UWord) states) & ~ERTS_CACHE_LINE_MASK)
			      + ERTS_CACHE_LINE_SIZE)
		  : (char *) states);
	tspec->allctr[0] = (Allctr_t *) state;
	size = tspec->size;
	for (i = 1; i < size; i++)
	    tspec->allctr[i] = (Allctr_t *)
		&((ErtsAllocatorState_t *) states)[i-1];
    }

    if (init->init.util.fix_type_size) {
	size_t tot_fix_list_size;
	fix_list_size = sizeof(ErtsAlcFixList_t)*ERTS_ALC_NO_FIXED_SIZES;
	fix_list_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(fix_list_size);
	tot_fix_list_size = fix_list_size;
	if (init->thr_spec)
	    tot_fix_list_size *= tspec->size;
	fix_lists = erts_sys_alloc(0,
				   NULL,
				   (tot_fix_list_size
				    + ERTS_CACHE_LINE_SIZE - 1));
	if (!fix_lists)
	    erts_exit(ERTS_ABORT_EXIT,
		     "Failed to allocate fix lists for %salloc\n",
		     init->init.util.name_prefix);

	if (((UWord) fix_lists) & ERTS_CACHE_LINE_MASK)
		fix_lists = ((ErtsAlcFixList_t *)
		       ((((UWord) fix_lists) & ~ERTS_CACHE_LINE_MASK)
			+ ERTS_CACHE_LINE_SIZE));
    }

    for (i = 0; i < size; i++) {
	Allctr_t *as;
	astrat = init->astrat;

	if (!init->thr_spec)
	    as0 = state;
	else {
	    as0 = (void *) tspec->allctr[i];
	    if (!as0)
		continue;
	    if (init->thr_spec < 0) {
		init->init.util.ts = (i == 0 || erts_no_schedulers < i);
		init->init.util.tspec = 0;
		init->init.util.tpref = -1*init->thr_spec + 1;
	    }
	    else {
		if (0 < i && i <= erts_no_schedulers)
		    init->init.util.ts = 0;
		else {
		    if (astrat == ERTS_ALC_S_AFIT)
			astrat = ERTS_ALC_S_GOODFIT;
		    init->init.util.ts = 1;
		}
		init->init.util.tspec = init->thr_spec + 1;
		init->init.util.tpref = 0;
	    }   
	}

	if (fix_lists) {
	    init->init.util.fix = fix_lists;
	    fix_lists = ((ErtsAlcFixList_t *)
			 (((char *) fix_lists) + fix_list_size));
	}

        init->init.util.alloc_strat = astrat;
	init->init.util.ix = i;

	switch (astrat) {
	case ERTS_ALC_S_GOODFIT:
	    as = erts_gfalc_start((GFAllctr_t *) as0,
					   &init->init.gf,
					   &init->init.util);
	    break;
	case ERTS_ALC_S_BESTFIT:
	    as = erts_bfalc_start((BFAllctr_t *) as0,
					   &init->init.bf,
					   &init->init.util);
	    break;
	case ERTS_ALC_S_AFIT:
	    as = erts_afalc_start((AFAllctr_t *) as0,
					   &init->init.af,
					   &init->init.util);
	    break;
	case ERTS_ALC_S_FIRSTFIT:
	    as = erts_aoffalc_start((AOFFAllctr_t *) as0,
					     &init->init.aoff,
					     &init->init.util);
	    break;

	default:
	    as = NULL;
	    ASSERT(0);
	}

	if (!as)
	    erts_exit(ERTS_ABORT_EXIT,
		     "Failed to start %salloc\n", init->init.util.name_prefix);

	ASSERT(as == (void *) as0);
	af->extra = as;
    }

    if (init->thr_spec)
	af->extra = tspec;
    ai->extra = af->extra;
}


static void bad_param(char *param_start, char *param_end)
{
    size_t len = param_end - param_start;
    char param[100];
    if (len > 99)
	len = 99;
    sys_memcpy((void *) param, (void *) param_start, len);
    param[len] = '\0';
    erts_fprintf(stderr, "bad \"%s\" parameter\n", param);
    erts_usage();
}

static void bad_value(char *param_start, char *param_end, char *value)
{
    size_t len = param_end - param_start;
    char param[100];
    if (len > 99)
	len = 99;
    sys_memcpy((void *) param, (void *) param_start, len);
    param[len] = '\0';
    erts_fprintf(stderr, "bad \"%s\" value: %s\n", param, value);
    erts_usage();
}

/* Get arg marks argument as handled by
   putting NULL in argv */
static char *
get_value(char* rest, char** argv, int* ip)
{
    char *param = argv[*ip]+1;
    argv[*ip] = NULL;
    if (*rest == '\0') {
	char *next = argv[*ip + 1];
	if (next[0] == '-'
	    && next[1] == '-'
	    &&  next[2] == '\0') {
	    bad_value(param, rest, "");
	}
	(*ip)++;
	argv[*ip] = NULL;
	return next;
    }
    return rest;
}

static ERTS_INLINE int
has_prefix(const char *prefix, const char *string)
{
    int i;
    for (i = 0; prefix[i]; i++)
	if (prefix[i] != string[i])
	    return 0;
    return 1;
}

static int
get_bool_value(char *param_end, char** argv, int* ip)
{
    char *param = argv[*ip]+1;
    char *value = get_value(param_end, argv, ip);
    if (sys_strcmp(value, "true") == 0)
	return 1;
    else if (sys_strcmp(value, "false") == 0)
	return 0;
    else
	bad_value(param, param_end, value);
    return -1;
}

static Uint kb_to_bytes(Sint kb, Uint *bytes)
{
    const Uint max = ((~((Uint) 0))/1024) + 1;

    if (kb < 0 || (Uint)kb > max)
        return 0;
    if ((Uint)kb == max)
        *bytes = ~((Uint) 0);
    else
        *bytes = ((Uint) kb)*1024;
    return 1;
}

static Uint
get_kb_value(char *param_end, char** argv, int* ip)
{
    Sint tmp;
    Uint bytes = 0;
    char *rest;
    char *param = argv[*ip]+1;
    char *value = get_value(param_end, argv, ip);
    errno = 0;
    tmp = (Sint) ErtsStrToSint(value, &rest, 10);
    if (errno != 0 || rest == value || !kb_to_bytes(tmp, &bytes))
	bad_value(param, param_end, value);
    return bytes;
}

static UWord
get_mb_value(char *param_end, char** argv, int* ip)
{
    SWord tmp;
    UWord max = ((~((UWord) 0))/(1024*1024)) + 1;
    char *rest;
    char *param = argv[*ip]+1;
    char *value = get_value(param_end, argv, ip);
    errno = 0;
    tmp = (SWord) ErtsStrToSint(value, &rest, 10);
    if (errno != 0 || rest == value || tmp < 0 || max < ((UWord) tmp))
	bad_value(param, param_end, value);
    if (max == (UWord) tmp)
	return ~((UWord) 0);
    else
	return ((UWord) tmp)*1024*1024;
}


#if 0
static Uint
get_byte_value(char *param_end, char** argv, int* ip)
{
    Sint tmp;
    char *rest;
    char *param = argv[*ip]+1;
    char *value = get_value(param_end, argv, ip);
    errno = 0;
    tmp = (Sint) ErtsStrToSint(value, &rest, 10);
    if (errno != 0 || rest == value || tmp < 0)
	bad_value(param, param_end, value);
    return (Uint) tmp;
}
#endif

static Uint
get_amount_value(char *param_end, char** argv, int* ip)
{
    Sint tmp;
    char *rest;
    char *param = argv[*ip]+1;
    char *value = get_value(param_end, argv, ip);
    errno = 0;
    tmp = (Sint) ErtsStrToSint(value, &rest, 10);
    if (errno != 0 || rest == value || tmp < 0)
	bad_value(param, param_end, value);
    return (Uint) tmp;
}

static Uint
get_acul_value(struct au_init *auip, char *param_end, char** argv, int* ip)
{
    Sint tmp;
    char *rest;
    char *param = argv[*ip]+1;
    char *value = get_value(param_end, argv, ip);
    if (sys_strcmp(value, "de") == 0) {
	switch (auip->init.util.alloc_no) {
	case ERTS_ALC_A_LONG_LIVED:
	    return ERTS_ALC_DEFAULT_ENABLED_ACUL_LL_ALLOC;
	case ERTS_ALC_A_EHEAP:
	    return ERTS_ALC_DEFAULT_ENABLED_ACUL_EHEAP_ALLOC;
	default:
	    return ERTS_ALC_DEFAULT_ENABLED_ACUL;
	}
    }
    errno = 0;
    tmp = (Sint) ErtsStrToSint(value, &rest, 10);
    if (errno != 0 || rest == value || tmp < 0 || 100 < tmp)
	bad_value(param, param_end, value);
    return (Uint) tmp;
}

static void
handle_au_arg(struct au_init *auip,
	      char* sub_param,
	      char** argv,
	      int* ip,
	      int u_switch)
{
    char *param = argv[*ip]+1;

    switch (sub_param[0]) {
    case 'a':
        if (sub_param[1] == 'c') { /* Migration parameters "ac*" */
            UWord value;
            UWord* wp;
            if (!auip->carrier_migration_allowed && !u_switch)
                goto bad_switch;

            if (has_prefix("acul", sub_param)) {
                value = get_acul_value(auip, sub_param + 4, argv, ip);
                wp = &auip->init.util.acul;
            }
            else if (has_prefix("acnl", sub_param)) {
                value = get_amount_value(sub_param + 4, argv, ip);
                wp = &auip->init.util.acnl;
            }
            else if (has_prefix("acfml", sub_param)) {
                value = get_amount_value(sub_param + 5, argv, ip);
                wp = &auip->init.util.acfml;
            }
            else
                goto bad_switch;

            if (auip->carrier_migration_allowed)
                *wp = value;
        }
	else if(has_prefix("asbcst", sub_param)) {
	    auip->init.util.asbcst = get_kb_value(sub_param + 6, argv, ip);
	}
	else if(has_prefix("as", sub_param)) {
	    char *alg = get_value(sub_param + 2, argv, ip);
	    if (sys_strcmp("bf", alg) == 0) {
		auip->astrat = ERTS_ALC_S_BESTFIT;
		auip->init.bf.ao = 0;
	    }
	    else if (sys_strcmp("aobf", alg) == 0) {
		auip->astrat = ERTS_ALC_S_BESTFIT;
		auip->init.bf.ao = 1;
	    }
	    else if (sys_strcmp("gf", alg) == 0) {
		auip->astrat = ERTS_ALC_S_GOODFIT;
	    }
	    else if (sys_strcmp("af", alg) == 0) {
		auip->astrat = ERTS_ALC_S_AFIT;
	    }
	    else if (sys_strcmp("aoff", alg) == 0) {
		auip->astrat = ERTS_ALC_S_FIRSTFIT;
		auip->init.aoff.crr_order = FF_AOFF;
		auip->init.aoff.blk_order = FF_AOFF;
	    }
	    else if (sys_strcmp("aoffcbf", alg) == 0) {
		auip->astrat = ERTS_ALC_S_FIRSTFIT;
		auip->init.aoff.crr_order = FF_AOFF;
		auip->init.aoff.blk_order = FF_BF;
	    }
	    else if (sys_strcmp("aoffcaobf", alg) == 0) {
		auip->astrat = ERTS_ALC_S_FIRSTFIT;
		auip->init.aoff.crr_order = FF_AOFF;
		auip->init.aoff.blk_order = FF_AOBF;
	    }
            else if (sys_strcmp("ageffcaoff", alg) == 0) {
                auip->astrat = ERTS_ALC_S_FIRSTFIT;
		auip->init.aoff.crr_order = FF_AGEFF;
		auip->init.aoff.blk_order = FF_AOFF;
            }
            else if (sys_strcmp("ageffcbf", alg) == 0) {
                auip->astrat = ERTS_ALC_S_FIRSTFIT;
		auip->init.aoff.crr_order = FF_AGEFF;
		auip->init.aoff.blk_order = FF_BF;
            }
            else if (sys_strcmp("ageffcaobf", alg) == 0) {
                auip->astrat = ERTS_ALC_S_FIRSTFIT;
		auip->init.aoff.crr_order = FF_AGEFF;
		auip->init.aoff.blk_order = FF_AOBF;
            }
	    else {
                if (auip->init.util.alloc_no == ERTS_ALC_A_TEST
                    && sys_strcmp("chaosff", alg) == 0) {
                    auip->astrat = ERTS_ALC_S_FIRSTFIT;
                    auip->init.aoff.crr_order = FF_CHAOS;
                    auip->init.aoff.blk_order = FF_CHAOS;
                }
                else {
                    bad_value(param, sub_param + 1, alg);
                }
	    }
	    if (!strategy_support_carrier_migration(auip))
		auip->init.util.acul = 0;
	} else if (has_prefix("atags", sub_param)) {
            auip->init.util.atags = get_bool_value(sub_param + 5, argv, ip);
        }
	else
	    goto bad_switch;
	break;
    case 'c': {
        if (has_prefix("cp", sub_param)) {
            char *param, *param_end, *value;
            int cp;
            if (!auip->carrier_migration_allowed && !u_switch)
                goto bad_switch;
            param = argv[*ip]+1;
            param_end = sub_param + 2;
            value = get_value(param_end, argv, ip);
            if (value[0] == '\0' || value[1] != '\0')
                bad_value(param, param_end, value);
            switch (value[0]) {
            case 'B': cp = ERTS_ALC_A_BINARY; break;
            case 'D': cp = ERTS_ALC_A_STANDARD; break;
            case 'E': cp = ERTS_ALC_A_ETS; break;
            case 'F': cp = ERTS_ALC_A_FIXED_SIZE; break;
            case 'H': cp = ERTS_ALC_A_EHEAP; break;
            case 'L': cp = ERTS_ALC_A_LONG_LIVED; break;
            case 'R': cp = ERTS_ALC_A_DRIVER; break;
            case 'S': cp = ERTS_ALC_A_SHORT_LIVED; break;
            case '@': cp = ERTS_ALC_COMMON_CPOOL_IX; break;
            case ':': cp = auip->init.util.alloc_no; break;
            default:  cp = -1;
                bad_value(param, param_end, value);
                break;
            }
            if (auip->carrier_migration_allowed)
                auip->init.util.cp = cp;
        }
        else
            goto bad_switch;
        break;
    }
    case 'e': {
	int e = get_bool_value(sub_param + 1, argv, ip);
        if (!auip->disable_allowed && !e) {
            if (!u_switch)
                bad_value(param, sub_param + 1, "false");
	    else
		ASSERT(auip->enable); /* ignore */
        }
	else auip->enable = e;
	break;
    }
    case 'l':
	if (has_prefix("lmbcs", sub_param)) {
	    auip->default_.lmbcs = 0;
	    auip->init.util.lmbcs = get_kb_value(sub_param + 5, argv, ip);
	}
	else
	    goto bad_switch;
	break;
    case 'm':
	if (has_prefix("mbcgs", sub_param)) {
	    auip->init.util.mbcgs = get_amount_value(sub_param + 5, argv, ip);

	}
	else if (has_prefix("mbsd", sub_param)) {
	    auip->init.gf.mbsd = get_amount_value(sub_param + 4, argv, ip);
	    if (auip->init.gf.mbsd < 1)
		auip->init.gf.mbsd = 1;
	}
	else if (has_prefix("mmbcs", sub_param)) {
	    auip->default_.mmbcs = 0;
	    auip->init.util.mmbcs = get_kb_value(sub_param + 5, argv, ip);
	}
	else if (has_prefix("mmmbc", sub_param)) {
	    auip->default_.mmmbc = 0;
	    auip->init.util.mmmbc = get_amount_value(sub_param + 5, argv, ip);
	}
	else if (has_prefix("mmsbc", sub_param)) {
	    auip->init.util.mmsbc = get_amount_value(sub_param + 5, argv, ip);
	}
	else
	    goto bad_switch;
	break;
    case 'r':
	if(has_prefix("rsbcmt", sub_param)) {
	    auip->init.util.rsbcmt = get_amount_value(sub_param + 6, argv, ip);
	    if (auip->init.util.rsbcmt > 100)
		auip->init.util.rsbcmt = 100;
	}
	else if(has_prefix("rsbcst", sub_param)) {
	    auip->init.util.rsbcst = get_amount_value(sub_param + 6, argv, ip);
	    if (auip->init.util.rsbcst > 100)
		auip->init.util.rsbcst = 100;
	}
	else if (has_prefix("rmbcmt", sub_param)) {
	    auip->init.util.rmbcmt = get_amount_value(sub_param + 6, argv, ip);
	    if (auip->init.util.rmbcmt > 100)
		auip->init.util.rmbcmt = 100;
	}
	else if (has_prefix("ramv", sub_param)) {
	    auip->init.util.ramv = get_bool_value(sub_param + 4, argv, ip);
	}
	else
	    goto bad_switch;
	break;
    case 's':
	if(has_prefix("sbct", sub_param)) {
	    auip->init.util.sbct = get_kb_value(sub_param + 4, argv, ip);
	}
	else if (has_prefix("smbcs", sub_param)) {
	    auip->default_.smbcs = 0;
	    auip->init.util.smbcs = get_kb_value(sub_param + 5, argv, ip);
	}
	else
	    goto bad_switch;
	break;
    case 't': {
	int res = get_bool_value(sub_param+1, argv, ip);
	if (res > 0) {
	    if (!auip->thr_spec_allowed) {
		if (!u_switch)
                    bad_value(param, sub_param + 1, "true");
		else
		    ASSERT(!auip->thr_spec); /* ignore */
	    }
	    else
		auip->thr_spec = 1;
	    break;
	}
	else if (res == 0) {
	    auip->thr_spec = 0;
	    auip->init.util.acul = 0;
	    break;
	}
	goto bad_switch;
    }
    default:
    bad_switch:
	bad_param(param, sub_param);
    }
}

static void
handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init)
{
    struct au_init *aui[] = {
	&init->binary_alloc,
	&init->std_alloc,
	&init->ets_alloc,
	&init->eheap_alloc,
	&init->ll_alloc,
	&init->driver_alloc,
	&init->fix_alloc,
	&init->sl_alloc
	/* test_alloc not affected by +Mea??? or +Mu???  */
    };
    int aui_sz = (int) sizeof(aui)/sizeof(aui[0]);
    char *arg;
    char *rest;
    int i, j;

    i = 1;

    ASSERT(argc && argv && init);

    while (i < *argc) {
	if(argv[i][0] == '-') {
	    char *param = argv[i]+1;
	    switch (argv[i][1]) {
	    case 'M':
		switch (argv[i][2]) {
		case 'B':
		    handle_au_arg(&init->binary_alloc, &argv[i][3], argv, &i, 0);
		    break;
                case 'I':
                    if (has_prefix("scs", argv[i]+3)) {
#if HAVE_ERTS_MSEG
			init->mseg.literal_mmap.scs =
#endif
			    get_mb_value(argv[i]+6, argv, &i);
		    }
                    else
                        handle_au_arg(&init->literal_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'D':
		    handle_au_arg(&init->std_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'E':
		    handle_au_arg(&init->ets_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'F':
		    handle_au_arg(&init->fix_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'H':
		    handle_au_arg(&init->eheap_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'L':
		    handle_au_arg(&init->ll_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'M':
		    if (has_prefix("amcbf", argv[i]+3)) {
#if HAVE_ERTS_MSEG
			init->mseg.amcbf =
#endif
			    get_kb_value(argv[i]+8, argv, &i);
		    }
		    else if (has_prefix("rmcbf", argv[i]+3)) {
#if HAVE_ERTS_MSEG
			init->mseg.rmcbf =
#endif
			    get_amount_value(argv[i]+8, argv, &i);
		    }
		    else if (has_prefix("mcs", argv[i]+3)) {
#if HAVE_ERTS_MSEG
			init->mseg.mcs =
#endif
			    get_amount_value(argv[i]+6, argv, &i);
		    }
		    else if (has_prefix("scs", argv[i]+3)) {
#if HAVE_ERTS_MSEG
			init->mseg.dflt_mmap.scs =
#endif
			    get_mb_value(argv[i]+6, argv, &i);
		    }
		    else if (has_prefix("sco", argv[i]+3)) {
#if HAVE_ERTS_MSEG
			init->mseg.dflt_mmap.sco =
#endif
			    get_bool_value(argv[i]+6, argv, &i);
		    }
		    else if (has_prefix("scrpm", argv[i]+3)) {
#if HAVE_ERTS_MSEG
			init->mseg.dflt_mmap.scrpm =
#endif
			    get_bool_value(argv[i]+8, argv, &i);
		    }
		    else if (has_prefix("scrfsd", argv[i]+3)) {
#if HAVE_ERTS_MSEG
			init->mseg.dflt_mmap.scrfsd =
#endif
			    get_amount_value(argv[i]+9, argv, &i);
		    }
		    else {
			bad_param(param, param+2);
		    }
		    break;
		case 'R':
		    handle_au_arg(&init->driver_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'S':
		    handle_au_arg(&init->sl_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'T':
		    handle_au_arg(&init->temp_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'Z':
		    handle_au_arg(&init->test_alloc, &argv[i][3], argv, &i, 0);
		    break;
		case 'Y': { /* sys_alloc */
		    if (has_prefix("tt", param+2)) {
			/* set trim threshold */
			arg = get_value(param+4, argv, &i);
			errno = 0;
			init->trim_threshold = (int) strtol(arg, &rest, 10);
			if (errno != 0
			    || rest == arg
			    || init->trim_threshold < 0
			    || (INT_MAX/1024) < init->trim_threshold) {
			    bad_value(param, param+4, arg);
			}
			VERBOSE(DEBUG_SYSTEM,
                                ("using trim threshold: %d\n",
                                 init->trim_threshold));
			init->trim_threshold *= 1024;
		    }
		    else if (has_prefix("tp", param+2)) {
			/* set top pad */
			arg = get_value(param+4, argv, &i);
			errno = 0;
			init->top_pad = (int) strtol(arg, &rest, 10);
			if (errno != 0
			    || rest == arg
			    || init->top_pad < 0
			    || (INT_MAX/1024) < init->top_pad) {
			    bad_value(param, param+4, arg);
			}
			VERBOSE(DEBUG_SYSTEM,
                                ("using top pad: %d\n",init->top_pad));
			init->top_pad *= 1024;
		    }
		    else if (has_prefix("m", param+2)) {
			/* Has been handled by erlexec */
			(void) get_value(param+3, argv, &i);
		    }
		    else if (has_prefix("e", param+2)) {
			arg = get_value(param+3, argv, &i);
			if (sys_strcmp("true", arg) != 0)
			    bad_value(param, param+3, arg);
		    }
		    else
			bad_param(param, param+2);
		    break;
		}
		case 'e':
		    switch (argv[i][3]) {
		    case 'a': {
			int a;
			arg = get_value(argv[i]+4, argv, &i);
			if (sys_strcmp("min", arg) == 0) {
			    for (a = 0; a < aui_sz; a++)
				aui[a]->enable = 0;
			}
			else if (sys_strcmp("max", arg) == 0) {
			    for (a = 0; a < aui_sz; a++)
				aui[a]->enable = 1;
			}
			else if (sys_strcmp("config", arg) == 0) {
			    init->erts_alloc_config = 1;
			}
			else if (sys_strcmp("r9c", arg) == 0
				 || sys_strcmp("r10b", arg) == 0
				 || sys_strcmp("r11b", arg) == 0) {
			    set_default_sl_alloc_opts(&init->sl_alloc);
			    set_default_std_alloc_opts(&init->std_alloc);
			    set_default_ll_alloc_opts(&init->ll_alloc);
			    set_default_temp_alloc_opts(&init->temp_alloc);
			    set_default_eheap_alloc_opts(&init->eheap_alloc);
			    set_default_binary_alloc_opts(&init->binary_alloc);
			    set_default_ets_alloc_opts(&init->ets_alloc);
			    set_default_driver_alloc_opts(&init->driver_alloc);
			    set_default_driver_alloc_opts(&init->fix_alloc);

			    init->driver_alloc.enable = 0;
			    if (sys_strcmp("r9c", arg) == 0) {
				init->sl_alloc.enable = 0;
				init->std_alloc.enable = 0;
				init->binary_alloc.enable = 0;
				init->ets_alloc.enable = 0;
			    }

			    for (a = 0; a < aui_sz; a++) {
				aui[a]->thr_spec = 0;
				aui[a]->init.util.acul = 0;
				aui[a]->init.util.ramv = 0;
				aui[a]->init.util.lmbcs = 5*1024*1024;
			    }
			}
			else {
			    bad_param(param, param+3);
			}
			break;
		    }
		    default:
			bad_param(param, param+1);
		    }
		    break;
		case 'l':
		    if (has_prefix("pm", param+2)) {
			arg = get_value(argv[i]+5, argv, &i);
			if (sys_strcmp("all", arg) == 0)
			    lock_all_physical_memory = 1;
			else if (sys_strcmp("no", arg) == 0)
			    lock_all_physical_memory = 0;
			else
			    bad_value(param, param+4, arg);
			break;
		    }
		    bad_param(param, param+2);
		    break;
		case 'd':
                    if (has_prefix("ai", param+2)) {
                        arg = get_value(argv[i]+5, argv, &i);
                        if (sys_strcmp("max", arg) == 0)
                            init->dirty_alloc_insts = (int) erts_no_dirty_cpu_schedulers;
                        else {
                            Sint tmp;
                            char *rest;
                            errno = 0;
                            tmp = (Sint) ErtsStrToSint(arg, &rest, 10);
                            if (errno != 0 || rest == arg || tmp < 0)
                                bad_value(param, param+4, arg);
                            init->dirty_alloc_insts = (int) tmp;
                        }
                        break;
                    }
                    bad_param(param, param+2);
		    break;
		case 'u':
		    if (has_prefix("ycs", argv[i]+3)) {
			init->alloc_util.ycs
			    = get_kb_value(argv[i]+6, argv, &i);
		    }
		    else if (has_prefix("mmc", argv[i]+3)) {
			init->alloc_util.mmc
			    = get_amount_value(argv[i]+6, argv, &i);
		    }
		    else if (has_prefix("sac", argv[i]+3)) {
			init->alloc_util.sac
			    = get_bool_value(argv[i]+6, argv, &i);
		    }
		    else {
			int a;
			int start = i;
			char *param = argv[i];
			char *val = i+1 < *argc ? argv[i+1] : NULL;

			for (a = 0; a < aui_sz; a++) {
			    if (a > 0) {
				ASSERT(i == start || i == start+1);
				argv[start] = param;
				if (i != start)
				    argv[start + 1] = val;
				i = start;
			    }
			    handle_au_arg(aui[a], &argv[i][3], argv, &i, 1);
			}
		    }
		    break;
		default:
		    bad_param(param, param+1);
		}
		break;
	    case '-':
		if (argv[i][2] == '\0') {
		    /* End of system flags reached */
		    goto args_parsed;
		}
		break;
	    default:
		break;
	    }
	}
	i++;
    }

 args_parsed:
    /* Handled arguments have been marked with NULL. Slide arguments
       not handled towards the beginning of argv. */
    for (i = 0, j = 0; i < *argc; i++) {
	if (argv[i])
	    argv[j++] = argv[i];
    }
    *argc = j;

    if (init->dirty_alloc_insts > erts_no_dirty_cpu_schedulers)
        init->dirty_alloc_insts = (int) erts_no_dirty_cpu_schedulers;

}

static char *type_no_str(ErtsAlcType_t n)
{

#if ERTS_ALC_N_MIN != 0
    if (n < ERTS_ALC_N_MIN)
	return NULL;
#endif
    if (n > ERTS_ALC_N_MAX)
	return NULL;
    return (char *) ERTS_ALC_N2TD(n);
}

#define type_str(T) type_no_str(ERTS_ALC_T2N((T)))

void
erts_alloc_register_scheduler(void *vesdp)
{
    ErtsSchedulerData *esdp = (ErtsSchedulerData *) vesdp;
    int ix;
    int aix;
    int normal_sched;

    ASSERT(esdp == erts_get_scheduler_data());

    if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) {
        ix = (int) esdp->no;
        ASSERT(0 < ix && ix <= erts_no_schedulers);
        normal_sched = !0;
    }
    else if (!erts_no_dirty_alloc_instances) {
        ix = 0;
        normal_sched = 0;
    }
    else {
        ix = (int) esdp->dirty_no;
        ASSERT(ix > 0);
        ix = ((ix - 1) % erts_no_dirty_alloc_instances) + 1;
        ix += erts_no_schedulers;
        ASSERT(erts_no_schedulers < ix
               && ix <= (erts_no_schedulers
                         + erts_no_dirty_alloc_instances));
        normal_sched = 0;
    }

    esdp->aux_work_data.alloc_data.delayed_dealloc_handler = normal_sched;
    esdp->aux_work_data.alloc_data.alc_ix = ix;
    for (aix = ERTS_ALC_A_MIN; aix <= ERTS_ALC_A_MAX; aix++) {
	ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[aix];
	esdp->aux_work_data.alloc_data.deallctr[aix] = NULL;
        if (!normal_sched)
            continue;
	/*
	 * Delayed dealloc is handled by normal schedulers,
	 * but not by dirty schedulers...
	 */
	if (tspec->enabled && tspec->dd) {
	    Allctr_t *allctr = tspec->allctr[ix];
	    ASSERT(allctr);
            esdp->aux_work_data.alloc_data.deallctr[aix] = allctr;
        }
    }
    erts_tsd_set(erts_thr_alloc_data_key, (void *) &esdp->aux_work_data.alloc_data);
}

void
erts_alloc_register_delayed_dealloc_handler_thread(ErtsThrAllocData *tadp, int ix)
{
    int aix;

    /*
     * Should not be a scheduler of any kind and should not
     * handle an 'ix' reserved for normal schedulers...
     */
    ASSERT(!erts_get_scheduler_data());
    ASSERT(0 == ix /* Aux thread... */
           || /* Handler thread for dirty scheds instances... */
           (erts_no_schedulers < ix
            && ix <= (erts_no_schedulers
                      + erts_no_dirty_alloc_instances)));

    tadp->delayed_dealloc_handler = !0;
    tadp->alc_ix = ix;
    for (aix = ERTS_ALC_A_MIN; aix <= ERTS_ALC_A_MAX; aix++) {
        ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[aix];
        tadp->deallctr[aix] = NULL;
        if (tspec->enabled && tspec->dd) {
            Allctr_t *allctr = tspec->allctr[ix];
            ASSERT(allctr);
            tadp->deallctr[aix] = allctr;
        }
    }
    erts_tsd_set(erts_thr_alloc_data_key, (void *) tadp);
}

void
erts_alloc_handle_delayed_dealloc(ErtsThrAllocData *thr_alloc_data,
				  int *need_thr_progress,
				  ErtsThrPrgrVal *thr_prgr_p,
				  int *more_work)
{
    int aix;
    ASSERT(thr_alloc_data);
    for (aix = ERTS_ALC_A_MIN; aix <= ERTS_ALC_A_MAX; aix++) {
	Allctr_t *allctr = thr_alloc_data->deallctr[aix];
	if (allctr) {
	    erts_alcu_check_delayed_dealloc(allctr,
					    1,
					    need_thr_progress,
					    thr_prgr_p,
					    more_work);
	}
    }
}

erts_aint32_t
erts_alloc_fix_alloc_shrink(int ix, erts_aint32_t flgs)
{
    ErtsAllocatorThrSpec_t *tspec;
    tspec = &erts_allctr_thr_spec[ERTS_ALC_A_FIXED_SIZE];
    if (erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].thr_spec && tspec->enabled)
	return erts_alcu_fix_alloc_shrink(tspec->allctr[ix], flgs);
    if (ix == 0 && erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].extra)
	return erts_alcu_fix_alloc_shrink(
	    erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].extra, flgs);
    return 0;
}

static void
no_verify(Allctr_t *allctr)
{

}

erts_alloc_verify_func_t
erts_alloc_get_verify_unused_temp_alloc(Allctr_t **allctr)
{
    if (erts_allctrs_info[ERTS_ALC_A_TEMPORARY].alloc_util
	&& erts_allctrs_info[ERTS_ALC_A_TEMPORARY].thr_spec) {
	ErtsAllocatorThrSpec_t *tspec;
        int ix = erts_get_thr_alloc_ix();
	tspec = &erts_allctr_thr_spec[ERTS_ALC_A_TEMPORARY];

	if (ix < tspec->size) {
	    *allctr = tspec->allctr[ix];
	    return erts_alcu_verify_unused;
	}
    }

    *allctr = NULL;
    return no_verify;
}

__decl_noreturn void
erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...)
{
    char buf[10];
    char *t_str;
    char *allctr_str;

    ASSERT(n >= ERTS_ALC_N_MIN);
    ASSERT(n <= ERTS_ALC_N_MAX);


    if (n < ERTS_ALC_N_MIN || ERTS_ALC_N_MAX < n)
	allctr_str = "UNKNOWN";
    else {
	ErtsAlcType_t a = ERTS_ALC_T2A(ERTS_ALC_N2T(n));
	if (erts_allctrs_info[a].enabled)
	    allctr_str = (char *) ERTS_ALC_A2AD(a);
	else
	    allctr_str = (char *) ERTS_ALC_A2AD(ERTS_ALC_A_SYSTEM);
    }

    t_str = type_no_str(n);
    if (!t_str) {
	erts_snprintf(buf, sizeof(buf), "%d", (int) n);
	t_str = buf;
    }

    switch (error) {
    case ERTS_ALC_E_NOTSUP: {
	char *op_str;
	switch (func) {
	case ERTS_ALC_O_ALLOC:		op_str = "alloc";	break;
	case ERTS_ALC_O_REALLOC:	op_str = "realloc";	break;
	case ERTS_ALC_O_FREE:		op_str = "free";	break;
	default:			op_str = "UNKNOWN";	break;
	}
	erts_exit(ERTS_ABORT_EXIT,
		 "%s: %s operation not supported (memory type: \"%s\")\n",
		 allctr_str, op_str, t_str);
	break;
    }
    case ERTS_ALC_E_NOMEM: {
	Uint size;
	va_list argp;
	char *op = func == ERTS_ALC_O_REALLOC ? "reallocate" : "allocate";
	

	va_start(argp, n);
	size = va_arg(argp, Uint);
	va_end(argp);
	erts_exit(ERTS_DUMP_EXIT,
		 "%s: Cannot %s %lu bytes of memory (of type \"%s\").\n",
		 allctr_str, op, size, t_str);
	break;
    }
    case ERTS_ALC_E_NOALLCTR:
	erts_exit(ERTS_ABORT_EXIT,
		 "erts_alloc: Unknown allocator type: %d\n",
		 ERTS_ALC_T2A(ERTS_ALC_N2T(n)));
	break;
    default:
	erts_exit(ERTS_ABORT_EXIT, "erts_alloc: Unknown error: %d\n", error);
	break;
    }
}

__decl_noreturn void
erts_alloc_enomem(ErtsAlcType_t type, Uint size)
{
    erts_alloc_n_enomem(ERTS_ALC_T2N(type), size);
}

__decl_noreturn void
erts_alloc_n_enomem(ErtsAlcType_t n, Uint size)
{
    erts_alc_fatal_error(ERTS_ALC_E_NOMEM, ERTS_ALC_O_ALLOC, n, size);
}

__decl_noreturn void
erts_realloc_enomem(ErtsAlcType_t type, void *ptr, Uint size)
{
    erts_realloc_n_enomem(ERTS_ALC_T2N(type), ptr, size);
}

__decl_noreturn void
erts_realloc_n_enomem(ErtsAlcType_t n, void *ptr, Uint size)
{
    erts_alc_fatal_error(ERTS_ALC_E_NOMEM, ERTS_ALC_O_REALLOC, n, size);
}

static ERTS_INLINE UWord
alcu_size(ErtsAlcType_t alloc_no, ErtsAlcUFixInfo_t *fi, int fisz)
{
    UWord res;
    int ai;

    if (!erts_allctrs_info[alloc_no].thr_spec) {
        AllctrSize_t size;
        Allctr_t *allctr;

        allctr = erts_allctrs_info[alloc_no].extra;
        erts_alcu_current_size(allctr, &size, fi, fisz);

        return size.blocks;
    }

    res = 0;

    /* Thread-specific allocators can migrate carriers across types, so we have
     * to visit every allocator type to gather information on blocks that were
     * allocated by us. */
    for (ai = ERTS_ALC_A_MIN; ai < ERTS_ALC_A_MAX; ai++) {
        ErtsAllocatorThrSpec_t *tspec;
        Allctr_t *allctr;
        int i;

        if (!erts_allctrs_info[ai].thr_spec) {
            continue;
        }

        tspec = &erts_allctr_thr_spec[ai];
        ASSERT(tspec->enabled);

        for (i = tspec->size - 1; i >= 0; i--) {
            allctr = tspec->allctr[i];

            if (allctr) {
                AllctrSize_t size;

                if (ai == alloc_no) {
                    erts_alcu_current_size(allctr, &size, fi, fisz);
                } else {
                    erts_alcu_foreign_size(allctr, alloc_no, &size);
                }

                /* Sanity check of block size on 64-bit */
                ASSERT(sizeof(UWord) == 4 || ((SWord)size.blocks) >= 0);

                res += size.blocks;
            }
        }
    }

    return res;
}

static ERTS_INLINE void
add_fix_values(UWord *ap, UWord *up, ErtsAlcUFixInfo_t *fi, ErtsAlcType_t type)
{
    int ix = ERTS_ALC_T2N(type) - ERTS_ALC_N_MIN_A_FIXED_SIZE;
    ASSERT(0 <= ix && ix < ERTS_ALC_NO_FIXED_SIZES);

    *ap += (UWord) fi[ix].allocated;
    *up += (UWord) fi[ix].used;
}

Eterm
erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
{
/*
 * NOTE! When updating this function, make sure to also update
 *       erlang:memory/[0,1] in $ERL_TOP/erts/preloaded/src/erlang.erl
 */
#define ERTS_MEM_NEED_ALL_ALCU (want_tot_or_sys)
    struct {
	int total;
	int processes;
	int processes_used;
	int system;
	int atom;
	int atom_used;
	int binary;
	int code;
	int ets;
    } want = {0};
    struct {
	UWord total;
	UWord processes;
	UWord processes_used;
	UWord system;
	UWord atom;
	UWord atom_used;
	UWord binary;
	UWord code;
	UWord ets;
    } size = {0};
    Eterm atoms[sizeof(size)/sizeof(UWord)];
    UWord *uintps[sizeof(size)/sizeof(UWord)];
    Eterm euints[sizeof(size)/sizeof(UWord)];
    int want_tot_or_sys;
    int length;
    Eterm res = THE_NON_VALUE;
    ErtsAlcType_t ai;
    int only_one_value = 0;
    ErtsAlcUFixInfo_t fi[ERTS_ALC_NO_FIXED_SIZES] = {{0,0}};

    ERTS_LC_ASSERT(erts_thr_progress_is_blocking());

    /* Figure out what's wanted... */

    length = 0;
    if (is_non_value(earg)) { /* i.e. wants all */
	want.total = 1;
	atoms[length] = am_total;
	uintps[length++] = &size.total;

	want.processes = 1;
	atoms[length] = am_processes;
	uintps[length++] = &size.processes;

	want.processes_used = 1;
	atoms[length] = am_processes_used;
	uintps[length++] = &size.processes_used;

	want.system = 1;
	atoms[length] = am_system;
	uintps[length++] = &size.system;

	want.atom = 1;
	atoms[length] = am_atom;
	uintps[length++] = &size.atom;

	want.atom_used = 1;
	atoms[length] = am_atom_used;
	uintps[length++] = &size.atom_used;

	want.binary = 1;
	atoms[length] = am_binary;
	uintps[length++] = &size.binary;

	want.code = 1;
	atoms[length] = am_code;
	uintps[length++] = &size.code;

	want.ets = 1;
	atoms[length] = am_ets;
	uintps[length++] = &size.ets;
    }
    else {
	DeclareTmpHeapNoproc(tmp_heap,2);
	Eterm wanted_list;

	if (is_nil(earg))
	    return NIL;

	UseTmpHeapNoproc(2);
	if (is_not_atom(earg))
	    wanted_list = earg;
	else {
	    wanted_list = CONS(&tmp_heap[0], earg, NIL);
	    only_one_value = 1;
	}
	    
	while (is_list(wanted_list)) {
	    switch (CAR(list_val(wanted_list))) {
	    case am_total:
		if (!want.total) {
		    want.total = 1;
		    atoms[length] = am_total;
		    uintps[length++] = &size.total;
		}
		break;
	    case am_processes:
		if (!want.processes) {
		    want.processes = 1;
		    atoms[length] = am_processes;
		    uintps[length++] = &size.processes;
		}
		break;
	    case am_processes_used:
		if (!want.processes_used) {
		    want.processes_used = 1;
		    atoms[length] = am_processes_used;
		    uintps[length++] = &size.processes_used;
		}
		break;
	    case am_system:
		if (!want.system) {
		    want.system = 1;
		    atoms[length] = am_system;
		    uintps[length++] = &size.system;
		}
		break;
	    case am_atom:
		if (!want.atom) {
		    want.atom = 1;
		    atoms[length] = am_atom;
		    uintps[length++] = &size.atom;
		}
		break;
	    case am_atom_used:
		if (!want.atom_used) {
		    want.atom_used = 1;
		    atoms[length] = am_atom_used;
		    uintps[length++] = &size.atom_used;
		}
		break;
	    case am_binary:
		if (!want.binary) {
		    want.binary = 1;
		    atoms[length] = am_binary;
		    uintps[length++] = &size.binary;
		}
		break;
	    case am_code:
		if (!want.code) {
		    want.code = 1;
		    atoms[length] = am_code;
		    uintps[length++] = &size.code;
		}
		break;
	    case am_ets:
		if (!want.ets) {
		    want.ets = 1;
		    atoms[length] = am_ets;
		    uintps[length++] = &size.ets;
		}
		break;
	    default:
		UnUseTmpHeapNoproc(2);
		return am_badarg;
	    }
	    wanted_list = CDR(list_val(wanted_list));
	}
	UnUseTmpHeapNoproc(2);
	if (is_not_nil(wanted_list))
	    return am_badarg;
    }

    /* All alloc_util allocators *have* to be enabled, except test_alloc */
    
    for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++) {
	switch (ai) {
	case ERTS_ALC_A_SYSTEM:
        case ERTS_ALC_A_TEST:
	    break;
	default:
	    if (!erts_allctrs_info[ai].enabled
		|| !erts_allctrs_info[ai].alloc_util) {
		return am_notsup;
	    }
	    break;
	}
    }

    ASSERT(length <= sizeof(atoms)/sizeof(Eterm));
    ASSERT(length <= sizeof(euints)/sizeof(Eterm));
    ASSERT(length <= sizeof(uintps)/sizeof(UWord));


    if (proc) {
	ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN
			   == erts_proc_lc_my_proc_locks(proc));
	/* We'll need locks early in the lock order */
	erts_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
    }

    /* Calculate values needed... */

    want_tot_or_sys = want.total || want.system;

    if (ERTS_MEM_NEED_ALL_ALCU) {
	size.total = 0;

	for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++) {
	    if (erts_allctrs_info[ai].alloc_util) {
		UWord *save;
		UWord asz;
		switch (ai) {
		case ERTS_ALC_A_TEMPORARY:
		     /*
		      * Often not thread safe and usually never
		      * contain any allocated memory.
		      */
		    continue;
                case ERTS_ALC_A_TEST:
                    continue;
		case ERTS_ALC_A_EHEAP:
		    save = &size.processes;
		    break;
		case ERTS_ALC_A_ETS:
		    save = &size.ets;
		    break;
		case ERTS_ALC_A_BINARY:
		    save = &size.binary;
		    break;
		case ERTS_ALC_A_FIXED_SIZE:
		    asz = alcu_size(ai, fi, ERTS_ALC_NO_FIXED_SIZES);
		    size.total += asz;
		    continue;
		default:
		    save = NULL;
		    break;
		}
		asz = alcu_size(ai, NULL, 0);
		if (save)
		    *save = asz;
		size.total += asz;
	    }
	}
    }



    if (want_tot_or_sys || want.processes || want.processes_used) {
	UWord tmp;

	if (ERTS_MEM_NEED_ALL_ALCU)
	    tmp = size.processes;
	else {
	    alcu_size(ERTS_ALC_A_FIXED_SIZE,
		      fi, ERTS_ALC_NO_FIXED_SIZES);
	    tmp = alcu_size(ERTS_ALC_A_EHEAP, NULL, 0);
	}
	tmp += erts_ptab_mem_size(&erts_proc);
	tmp += erts_bif_timer_memory_size();

	size.processes = size.processes_used = tmp;

	add_fix_values(&size.processes,
		       &size.processes_used,
		       fi,
		       ERTS_ALC_T_PROC);
	add_fix_values(&size.processes,
		       &size.processes_used,
		       fi,
		       ERTS_ALC_T_MONITOR);
	add_fix_values(&size.processes,
		       &size.processes_used,
		       fi,
		       ERTS_ALC_T_LINK);
	add_fix_values(&size.processes,
		       &size.processes_used,
		       fi,
		       ERTS_ALC_T_MSG_REF);
	add_fix_values(&size.processes,
		       &size.processes_used,
		       fi,
		       ERTS_ALC_T_LL_PTIMER);
	add_fix_values(&size.processes,
		       &size.processes_used,
		       fi,
		       ERTS_ALC_T_HL_PTIMER);
	add_fix_values(&size.processes,
		       &size.processes_used,
		       fi,
		       ERTS_ALC_T_BIF_TIMER);
    }

    if (want.atom || want.atom_used) {
	Uint reserved_atom_space, atom_space;
	erts_atom_get_text_space_sizes(&reserved_atom_space, &atom_space);
	size.atom = size.atom_used = atom_table_sz();

	if (want.atom)
	    size.atom += reserved_atom_space;

	if (want.atom_used)
	    size.atom_used += atom_space;
    }

    if (!ERTS_MEM_NEED_ALL_ALCU && want.binary)
	size.binary = alcu_size(ERTS_ALC_A_BINARY, NULL, 0);

    if (want.code) {
	size.code = module_table_sz();
	size.code += export_table_sz();
	size.code += export_entries_sz();
	size.code += erts_fun_table_sz();
	size.code += erts_ranges_sz();
	size.code += erts_total_code_size;
    }

    if (want.ets) {
	if (!ERTS_MEM_NEED_ALL_ALCU)
	    size.ets = alcu_size(ERTS_ALC_A_ETS, NULL, 0);
	size.ets += erts_get_ets_misc_mem_size();
    }

    if (want_tot_or_sys) {
#ifdef BEAMASM
        /* The JIT allocates code on its own because of W^X restrictions, so we
         * need to bump the total size accordingly. */
        size.total += erts_total_code_size;
#endif

        ASSERT(size.total >= size.processes);
	size.system = size.total - size.processes;
    }

    if (print_to_p) {
	int i;
	fmtfn_t to = *print_to_p;
	void *arg = print_to_arg;

	/* Print result... */
	erts_print(to, arg, "=memory\n");
	for (i = 0; i < length; i++)
	    erts_print(to, arg, "%T: %bpu\n", atoms[i], *uintps[i]);
    }

    if (proc) {
	/* Build erlang term result... */
	Uint *hp;
	Uint hsz;

	erts_proc_lock(proc, ERTS_PROC_LOCK_MAIN);

	if (only_one_value) {
	    ASSERT(length == 1);
	    hsz = 0;
	    erts_bld_uword(NULL, &hsz, *uintps[0]);
	    hp = hsz ? HAlloc((Process *) proc, hsz) : NULL;
	    res = erts_bld_uword(&hp, NULL, *uintps[0]);
	}
	else {
	    Uint **hpp = NULL;
	    Uint *hszp = &hsz;
	    hsz = 0;

	    while (1) {
		int i;
		for (i = 0; i < length; i++)
		    euints[i] = erts_bld_uword(hpp, hszp, *uintps[i]);
		res = erts_bld_2tup_list(hpp, hszp, length, atoms, euints);
		if (hpp)
		    break;
		hp = HAlloc((Process *) proc, hsz);
		hpp = &hp;
		hszp = NULL;
	    }
	}
    }

    return res;

#undef ERTS_MEM_NEED_ALL_ALCU
}

struct aa_values {
    Uint arity;
    const char *name;
    Uint ui[2];
};

Eterm
erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc)
{
#define MAX_AA_VALUES (24)
    struct aa_values values[MAX_AA_VALUES];
    Eterm res = THE_NON_VALUE;
    int i, length;
    Uint reserved_atom_space, atom_space;

    if (proc) {
	ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN
			   == erts_proc_lc_my_proc_locks(proc));

	/* We'll need locks early in the lock order */
	erts_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
    }

    i = 0;

    values[i].arity = 2;
    values[i].name = "sys_misc";
    values[i].ui[0] = erts_sys_misc_mem_sz();
    i++;

    values[i].arity = 2;
    values[i].name = "static";
    values[i].ui[0] =
	sizeof(ErtsPTab)*2			/* proc & port tables */
	+ erts_timer_wheel_memory_size();	/* Timer wheel */
    i++;

    erts_atom_get_text_space_sizes(&reserved_atom_space, &atom_space);

    values[i].arity = 3;
    values[i].name = "atom_space";
    values[i].ui[0] = reserved_atom_space;
    values[i].ui[1] = atom_space;
    i++;

    values[i].arity = 2;
    values[i].name = "atom_table";
    values[i].ui[0] = atom_table_sz();
    i++;

    values[i].arity = 2;
    values[i].name = "module_table";
    values[i].ui[0] = module_table_sz();
    i++;

    values[i].arity = 2;
    values[i].name = "export_table";
    values[i].ui[0] = export_table_sz();
    i++;

    values[i].arity = 2;
    values[i].name = "export_list";
    values[i].ui[0] = export_entries_sz();
    i++;

    values[i].arity = 2;
    values[i].name = "register_table";
    values[i].ui[0] = process_reg_sz();
    i++;

    values[i].arity = 2;
    values[i].name = "fun_table";
    values[i].ui[0] = erts_fun_table_sz();
    i++;

    values[i].arity = 2;
    values[i].name = "module_refs";
    values[i].ui[0] = erts_ranges_sz();
    i++;

    values[i].arity = 2;
    values[i].name = "loaded_code";
    values[i].ui[0] = erts_total_code_size;
    i++;

    values[i].arity = 2;
    values[i].name = "dist_table";
    values[i].ui[0] = erts_dist_table_size();
    i++;

    values[i].arity = 2;
    values[i].name = "node_table";
    values[i].ui[0] = erts_node_table_size();
    i++;

    values[i].arity = 2;
    values[i].name = "bits_bufs_size";
    values[i].ui[0] = erts_bits_bufs_size();
    i++;

    values[i].arity = 2;
    values[i].name = "bif_timer";
    values[i].ui[0] = erts_bif_timer_memory_size();
    i++;

    values[i].arity = 2;
    values[i].name = "process_table";
    values[i].ui[0] = erts_ptab_mem_size(&erts_proc);
    i++;

    values[i].arity = 2;
    values[i].name = "port_table";
    values[i].ui[0] = erts_ptab_mem_size(&erts_port);
    i++;

    values[i].arity = 2;
    values[i].name = "ets_misc";
    values[i].ui[0] = erts_get_ets_misc_mem_size();
    i++;

    /* Data not allocated by any alloc_util allocators, must be summed into
     * the "total" figure in erlang:memory/0,1. */
    values[i].arity = 2;
    values[i].name = "external_alloc";
#ifdef BEAMASM
    values[i].ui[0] = erts_total_code_size;
#else
    values[i].ui[0] = 0;
#endif
    i++;

    length = i;
    ASSERT(length <= MAX_AA_VALUES);

    if (print_to_p) {
	/* Print result... */
	fmtfn_t to = *print_to_p;
	void *arg = print_to_arg;

	erts_print(to, arg, "=allocated_areas\n");
	for (i = 0; i < length; i++) {
	    switch (values[i].arity) {
	    case 2:
		erts_print(to, arg, "%s: %beu\n",
			   values[i].name, values[i].ui[0]);
		break;
	    case 3:
		erts_print(to, arg, "%s: %beu %beu\n",
			   values[i].name, values[i].ui[0], values[i].ui[1]);
		break;
	    default:
		erts_print(to, arg, "ERROR: internal_error\n");
		ASSERT(0);
		return am_internal_error;
	    }
	}
    }

    if (proc) {
	/* Build erlang term result... */
	Eterm tuples[MAX_AA_VALUES];
	Uint *hp;
	Uint **hpp;
	Uint hsz;
	Uint *hszp;

	erts_proc_lock(proc, ERTS_PROC_LOCK_MAIN);

	hpp = NULL;
	hsz = 0;
	hszp = &hsz;

	while (1) {
	    int i;
	    for (i = 0; i < length; i++) {
		Eterm atom;
		if (hpp)
		    atom = am_atom_put(values[i].name,
				       (int) sys_strlen(values[i].name));
		else
		    atom = am_true;

		switch (values[i].arity) {
		case 2:
		    tuples[i] = erts_bld_tuple(hpp, hszp, 2,
					       atom,
					       erts_bld_uint(hpp, hszp,
							     values[i].ui[0]));
		    break;
		case 3:
		    tuples[i] = erts_bld_tuple(hpp, hszp, 3,
					       atom,
					       erts_bld_uint(hpp, hszp,
							     values[i].ui[0]),
					       erts_bld_uint(hpp, hszp,
							     values[i].ui[1]));
		    break;
		default:
		    ASSERT(0);
		    return am_internal_error;
		}
	    }
	    res = erts_bld_list(hpp, hszp, length, tuples);
	    if (hpp)
		break;
	    hp = HAlloc((Process *) proc, hsz);
	    hpp = &hp;
	    hszp = NULL;
	}
    }

    return res;
#undef MAX_AA_VALUES
}

Eterm
erts_alloc_util_allocators(void *proc)
{
    Eterm res;
    Uint *hp;
    Uint sz;
    int i;
    /*
     * Currently all allocators except sys_alloc are
     * alloc_util allocators.
     * Also hide test_alloc which is disabled by default
     * and only intended for our own testing.
     */
    sz = ((ERTS_ALC_A_MAX + 1 - ERTS_ALC_A_MIN) - 2)*2;
    ASSERT(sz > 0);
    hp = HAlloc((Process *) proc, sz);
    res = NIL;
    for (i = ERTS_ALC_A_MAX; i >= ERTS_ALC_A_MIN; i--) {
	switch (i) {
	case ERTS_ALC_A_SYSTEM:
        case ERTS_ALC_A_TEST:
	    break;
	default: {
	    char *alc_str = (char *) ERTS_ALC_A2AD(i);
	    Eterm alc = am_atom_put(alc_str, sys_strlen(alc_str));
	    res = CONS(hp, alc, res);
	    hp += 2;
	    break;
	}
	}
    }
    return res;
}

void
erts_allocator_info(fmtfn_t to, void *arg)
{
    ErtsAlcType_t a;

    ERTS_LC_ASSERT(erts_thr_progress_is_blocking());

    for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) {
	int ai;
	for (ai = 0; ai == 0 || ai < erts_allctrs_info[a].thr_spec; ai++) {
	    if (erts_allctrs_info[a].thr_spec) {
		if (!erts_allctr_thr_spec[a].allctr[ai])
		    continue;
		erts_print(to, arg, "=allocator:%s[%d]\n",
			   ERTS_ALC_A2AD(a), ai);
	    }
	    else {
		erts_print(to, arg, "=allocator:%s\n", ERTS_ALC_A2AD(a));
	    }
	    if (!erts_allctrs_info[a].enabled)
		erts_print(to, arg, "option e: false\n");
	    else {
		if (erts_allctrs_info[a].alloc_util) {
		    void *as;
		    if (!erts_allctrs_info[a].thr_spec)
			as = erts_allctrs_info[a].extra;
		    else {
			ASSERT(erts_allctr_thr_spec[a].enabled);
			as = erts_allctr_thr_spec[a].allctr[ai];
		    }
		    /* Binary alloc has its own thread safety... */
		    erts_alcu_info(as, 0, 0, &to, arg, NULL, NULL);
		}
		else {
		    switch (a) {
		    case ERTS_ALC_A_SYSTEM: {
			SysAllocStat sas;
			erts_print(to, arg, "option e: true\n");
			erts_print(to, arg, "option m: libc\n");
			sys_alloc_stat(&sas);
			if(sas.trim_threshold >= 0)
			    erts_print(to, arg, "option tt: %d\n", sas.trim_threshold);
			if(sas.top_pad >= 0)
			    erts_print(to, arg, "option tp: %d\n", sas.top_pad);
			break;
		    }
		    default:
			ASSERT(0);
			break;
		    }
		}
	    }
	}
    }

#if HAVE_ERTS_MSEG
    {
	struct erts_mmap_info_struct emis;
	int max = (int) erts_no_schedulers;
	int i;
	for (i = 0; i <= max; i++) {
	    erts_print(to, arg, "=allocator:mseg_alloc[%d]\n", i);
	    erts_mseg_info(i, &to, arg, 0, 0, NULL, NULL);
	}
	erts_print(to, arg, "=allocator:erts_mmap.default_mmap\n");
	erts_mmap_info(&erts_dflt_mmapper, &to, arg, NULL, NULL, &emis);
#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
        erts_print(to, arg, "=allocator:erts_mmap.literal_mmap\n");
        erts_mmap_info(&erts_literal_mmapper, &to, arg, NULL, NULL, &emis);
#endif
    }
#endif

    erts_print(to, arg, "=allocator:alloc_util\n");
    erts_alcu_au_info_options(&to, arg, NULL, NULL);

    erts_print(to, arg, "=allocator:instr\n");

}

Eterm
erts_allocator_options(void *proc)
{
#if HAVE_ERTS_MSEG
    int use_mseg = 0;
#endif
    Uint sz, *szp, *hp, **hpp;
    Eterm res, features, settings;
    Eterm atoms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+7];
    Uint terms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+7];
    int a, length;
    SysAllocStat sas;
    Uint *endp = NULL;

    sys_alloc_stat(&sas);

    /* First find out the heap size needed ... */
    hpp = NULL;
    szp = &sz;
    sz = 0;

 bld_term:

    length = 0;
    features = NIL;
    settings = NIL;

    for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) {
	Eterm tmp = NIL;
	atoms[length] = am_atom_put((char *) ERTS_ALC_A2AD(a),
				    sys_strlen(ERTS_ALC_A2AD(a)));
	if (erts_allctrs_info[a].enabled) {
	    if (erts_allctrs_info[a].alloc_util) {
		Allctr_t *allctr;
#if HAVE_ERTS_MSEG
		use_mseg++;
#endif
		if (erts_allctr_thr_spec[a].enabled)
		    allctr = erts_allctr_thr_spec[a].allctr[0];
		else
		    allctr = erts_allctrs_info[a].extra;
		tmp = erts_alcu_info_options(allctr, NULL, NULL, hpp, szp);
	    }
	    else {
		int l = 0;
		Eterm as[4];
		Eterm ts[4];

		as[l] = ERTS_MAKE_AM("e");
		ts[l++] = am_true;

		switch (a) {
		case ERTS_ALC_A_SYSTEM:
		    as[l] = ERTS_MAKE_AM("m");
		    ts[l++] = ERTS_MAKE_AM("libc");
		    if(sas.trim_threshold >= 0) {
			as[l] = ERTS_MAKE_AM("tt");
			ts[l++] = erts_bld_uint(hpp, szp,
						(Uint) sas.trim_threshold);
		    }
		    if(sas.top_pad >= 0) {
			as[l] = ERTS_MAKE_AM("tp");
			ts[l++] = erts_bld_uint(hpp, szp, (Uint) sas.top_pad);
		    }
		    break;
		default:
		    break;
		}

		tmp = erts_bld_2tup_list(hpp, szp, l, as, ts);

	    }

	}
	else {
	    Eterm atom = ERTS_MAKE_AM("e");
	    Eterm term = am_false;
	    tmp = erts_bld_2tup_list(hpp, szp, 1, &atom, &term);
	}

	terms[length++] = tmp;

    }

#if HAVE_ERTS_MSEG
    if (use_mseg) {
	atoms[length] = ERTS_MAKE_AM("mseg_alloc");
	terms[length++] = erts_mseg_info_options(0, NULL, NULL, hpp, szp);
    }
#endif

    atoms[length] = ERTS_MAKE_AM("alloc_util");
    terms[length++] = erts_alcu_au_info_options(NULL, NULL, hpp, szp);

#if HAVE_ERTS_MMAP
    atoms[length] = ERTS_MAKE_AM("erts_mmap");
    terms[length++] = erts_mmap_info_options(&erts_dflt_mmapper, NULL, NULL,
                                             NULL, hpp, szp);
#endif

    atoms[length] = ERTS_MAKE_AM("lock_physical_memory");
    terms[length++] = (lock_all_physical_memory ? am_all : am_no);

    settings = erts_bld_2tup_list(hpp, szp, length, atoms, terms);

    length = 0;

    for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) {
	if (erts_allctrs_info[a].enabled) {
	    terms[length++] = am_atom_put((char *) ERTS_ALC_A2AD(a),
					  sys_strlen(ERTS_ALC_A2AD(a)));
	}
    }

#if HAVE_ERTS_MSEG
    if (use_mseg)
	terms[length++] = ERTS_MAKE_AM("mseg_alloc");
#endif
#if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
    terms[length++] = ERTS_MAKE_AM("sys_aligned_alloc");
#endif
#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
    terms[length++] = ERTS_MAKE_AM("literal_mmap");
#endif
    features = length ? erts_bld_list(hpp, szp, length, terms) : NIL;

#if defined(__GLIBC__)
    {
	Eterm AM_glibc = ERTS_MAKE_AM("glibc");
	Eterm version;

	version = erts_bld_cons(hpp,
				szp,
				make_small(__GLIBC__),
#ifdef __GLIBC_MINOR__
				erts_bld_cons(hpp,
					      szp,
					      make_small(__GLIBC_MINOR__),
					      NIL)
#else
				NIL
#endif
	    );

	res = erts_bld_tuple(hpp, szp, 4,
			     AM_glibc, version, features, settings);
    }

#else /* unknown allocator */

    res = erts_bld_tuple(hpp, szp, 4,
			 am_undefined, NIL, features, settings);

#endif

    if (szp) {
	/* ... and then build the term */
	hp = HAlloc((Process *) proc, sz);
	endp = hp + sz;
	hpp = &hp;
	szp = NULL;
	goto bld_term;
    }

    ASSERT(endp >= hp);
    HRelease((Process *) proc, endp, hp);

    return res;
}

void *erts_alloc_permanent_cache_aligned(ErtsAlcType_t type, Uint size)
{
    UWord v = (UWord) erts_alloc(type, size + (ERTS_CACHE_LINE_SIZE-1)
#ifdef VALGRIND
				  + sizeof(UWord)
#endif
				 );

#ifdef VALGRIND
    {   /* Link them to avoid Leak_PossiblyLost */
	static UWord* first_in_list = NULL;
        *(UWord**)v = first_in_list;
	first_in_list = (UWord*) v;
	v += sizeof(UWord);
    }
#endif

    if (v & ERTS_CACHE_LINE_MASK) {
	v = (v & ~ERTS_CACHE_LINE_MASK) + ERTS_CACHE_LINE_SIZE;
    }
    ASSERT((v & ERTS_CACHE_LINE_MASK) == 0);
    return (void*)v;
}

static void
reply_alloc_info(void *vair)
{
    ErtsAllocInfoReq *air = (ErtsAllocInfoReq *) vair;
    int tix = erts_get_thr_alloc_ix();
    int global_instances = air->req_sched == tix;
    ErtsProcLocks rp_locks;
    Process *rp = air->proc;
    Eterm ref_copy = NIL, ai_list, msg = NIL;
    Eterm *hp = NULL, *hp_start = NULL, *hp_end = NULL;
    Eterm **hpp;
    Uint sz, *szp;
    ErlOffHeap *ohp = NULL;
    ErtsMessage *mp = NULL;
#if HAVE_ERTS_MMAP
    struct erts_mmap_info_struct mmap_info_dflt;
# if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
    struct erts_mmap_info_struct mmap_info_literal;
# endif
#endif
    int i;
    Eterm (*info_func)(Allctr_t *,
		       int,
		       int,
		       fmtfn_t *,
		       void *,
		       Uint **,
		       Uint *) = (air->only_sz
				  ? erts_alcu_sz_info
				  : erts_alcu_info);

    rp_locks = air->req_sched == tix ? ERTS_PROC_LOCK_MAIN : 0;

    sz = 0;
    hpp = NULL;
    szp = &sz;

    while (1) {

	if (hpp)
	    ref_copy = erts_iref_storage_make_ref(&air->iref,
                                                  hpp, ohp, 0);
	else
	    *szp += erts_iref_storage_heap_size(&air->iref);

	ai_list = NIL;
	for (i = 0; air->allocs[i] != ERTS_ALC_A_INVALID; i++);
	for (i--; i >= 0; i--) {
	    int ai = air->allocs[i];
	    Allctr_t *allctr;
	    Eterm ainfo;
	    Eterm alloc_atom;
	    if (global_instances) {
		switch (ai) {
		case ERTS_ALC_A_SYSTEM: {
		    alloc_atom = erts_bld_atom(hpp, szp, "sys_alloc");
		    ainfo = NIL;
		    if (!air->only_sz) {
			SysAllocStat sas;
			if (hpp)
			    sys_alloc_stat(&sas);
			if (szp) {
			    /* ensure enough heap */
			    sas.top_pad = INT_MAX;
			    sas.trim_threshold = INT_MAX;
			}
			if (sas.top_pad >= 0) {
			    ainfo = erts_bld_cons(
				hpp, szp,
				erts_bld_tuple(
				    hpp, szp, 2,
				    erts_bld_atom(hpp, szp, "tp"),
				    erts_bld_uint(
					hpp, szp,
					(Uint) sas.top_pad)),
				ainfo);
			}
			if (sas.trim_threshold >= 0) {
			    ainfo = erts_bld_cons(
				hpp, szp,
				erts_bld_tuple(
				    hpp, szp, 2,
				    erts_bld_atom(hpp, szp, "tt"),
				    erts_bld_uint(
					hpp, szp,
					(Uint) sas.trim_threshold)),
				ainfo);
			}
			ainfo = erts_bld_cons(hpp, szp,
					      erts_bld_tuple(
						  hpp, szp, 2,
						  erts_bld_atom(hpp, szp,
								"m"),
						  erts_bld_atom(hpp, szp,
								"libc")),
					      ainfo);
			ainfo = erts_bld_cons(hpp, szp,
					      erts_bld_tuple(
						  hpp, szp, 2,
						  erts_bld_atom(hpp, szp,
								"e"),
						  am_true),
					      ainfo);
			ainfo = erts_bld_tuple(hpp, szp, 2,
					       erts_bld_atom(hpp, szp,
							     "options"),
					       ainfo);
			ainfo = erts_bld_cons(hpp, szp,ainfo,NIL);
		    }
		    ainfo = erts_bld_tuple(hpp, szp, 3,
					   alloc_atom,
					   make_small(0),
					   ainfo);
		    break;
		}
		case ERTS_ALC_INFO_A_ALLOC_UTIL:
		    alloc_atom = erts_bld_atom(hpp, szp, "alloc_util");
		    ainfo = (air->only_sz
			     ? NIL
			     : erts_alcu_au_info_options(NULL, NULL,
							 hpp, szp));
		    ainfo = erts_bld_tuple(hpp, szp, 3,
					   alloc_atom,
					   make_small(0),
					   ainfo);
		    break;
                case ERTS_ALC_INFO_A_ERTS_MMAP:
                    alloc_atom = erts_bld_atom(hpp, szp, "erts_mmap");
#if HAVE_ERTS_MMAP
                    ainfo = (air->only_sz ? NIL :
                             erts_mmap_info(&erts_dflt_mmapper, NULL, NULL,
                                            hpp, szp, &mmap_info_dflt));
                    ainfo = erts_bld_tuple3(hpp, szp,
                                            alloc_atom,
                                            erts_bld_atom(hpp,szp,"default_mmap"),
                                            ainfo);
#  if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
                    ai_list = erts_bld_cons(hpp, szp,
                                            ainfo, ai_list);
                    ainfo = (air->only_sz ? NIL :
                             erts_mmap_info(&erts_literal_mmapper, NULL, NULL,
                                            hpp, szp, &mmap_info_literal));
                    ainfo = erts_bld_tuple3(hpp, szp,
                                            alloc_atom,
                                            erts_bld_atom(hpp,szp,"literal_mmap"),
                                            ainfo);
#  endif
#else  /* !HAVE_ERTS_MMAP */
                    ainfo = erts_bld_tuple2(hpp, szp, alloc_atom,
                                            am_false);
#endif
                    break;
		case ERTS_ALC_INFO_A_MSEG_ALLOC:
		    alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc");
#if HAVE_ERTS_MSEG
		    ainfo = erts_mseg_info(0, NULL, NULL, hpp != NULL,
                                           air->only_sz, hpp, szp);
		    ainfo = erts_bld_tuple3(hpp, szp,
                                            alloc_atom,
                                            make_small(0),
                                            ainfo);

#else
		    ainfo = erts_bld_tuple2(hpp, szp, alloc_atom,
                                            am_false);
#endif
		    break;
		default:
		    alloc_atom = erts_bld_atom(hpp, szp,
					       (char *) ERTS_ALC_A2AD(ai));
		    if (!erts_allctrs_info[ai].enabled) 
			ainfo = erts_bld_tuple(hpp, szp, 2, alloc_atom,
					       am_false);
		    else if (erts_allctrs_info[ai].alloc_util) {
			if (erts_allctrs_info[ai].thr_spec)
			    allctr = erts_allctr_thr_spec[ai].allctr[0];
			else
			    allctr = erts_allctrs_info[ai].extra;
			ainfo = info_func(allctr, air->internal, hpp != NULL,
					  NULL, NULL, hpp, szp);
			ainfo = erts_bld_tuple(hpp, szp, 3, alloc_atom,
					       make_small(0), ainfo);
		    }
		    else {
			erts_exit(ERTS_ABORT_EXIT, "%s:%d: internal error\n",
				 __FILE__, __LINE__);
		    }
		}
		ai_list = erts_bld_cons(hpp, szp,
					ainfo, ai_list);
	    }
	    switch (ai) {
	    case ERTS_ALC_A_SYSTEM:
            case ERTS_ALC_INFO_A_ALLOC_UTIL:
	    case ERTS_ALC_INFO_A_ERTS_MMAP:
		break;
	    case ERTS_ALC_INFO_A_MSEG_ALLOC:
#if HAVE_ERTS_MSEG
		alloc_atom = erts_bld_atom(hpp, szp, "mseg_alloc");
		ainfo = erts_mseg_info(tix, NULL, NULL,
                                       hpp != NULL, air->only_sz, hpp, szp);
		ainfo = erts_bld_tuple(hpp, szp, 3,
				       alloc_atom,
				       make_small(tix),
				       ainfo);
		ai_list = erts_bld_cons(hpp, szp, ainfo, ai_list);
#endif
		break;
	    default:
		if (erts_allctrs_info[ai].thr_spec) {
		    alloc_atom = erts_bld_atom(hpp, szp,
					       (char *) ERTS_ALC_A2AD(ai));
		    allctr = erts_allctr_thr_spec[ai].allctr[tix];
		    ainfo = info_func(allctr, air->internal, hpp != NULL, NULL,
				      NULL, hpp, szp);
		    ai_list = erts_bld_cons(hpp, szp,
					    erts_bld_tuple(
						hpp, szp,
						3,
						alloc_atom,
						make_small(tix),
						ainfo),
					    ai_list);
		}
		break;
	    }
	    msg = erts_bld_tuple(hpp, szp,
				 3,
				 ref_copy,
				 make_small(tix),
				 ai_list);

	}
	if (hpp)
	    break;

	mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
	hp_start = hp;
	hp_end = hp + sz;
	szp = NULL;
	hpp = &hp;
    }

    if (hp != hp_end)
	erts_shrink_message_heap(&mp, rp, hp_start, hp, hp_end, &msg, 1);

    erts_queue_message(rp, rp_locks, mp, msg, am_system);

    if (air->req_sched == tix)
	rp_locks &= ~ERTS_PROC_LOCK_MAIN;
 
    erts_proc_unlock(rp, rp_locks);
    erts_proc_dec_refc(rp);

    if (erts_atomic32_dec_read_nob(&air->refc) == 0) {
        erts_iref_storage_clean(&air->iref);
	aireq_free(air);
    }
}

int
erts_request_alloc_info(struct process *c_p,
			Eterm ref,
			Eterm allocs,
			int only_sz,
			int internal)
{
    ErtsAllocInfoReq *air = aireq_alloc();
    Eterm req_ai[ERTS_ALC_INFO_A_END] = {0};
    Eterm alist;
    int airix = 0, ai;

    air->req_sched = erts_get_scheduler_id();

    air->only_sz = only_sz;

    air->internal = internal;

    air->proc = c_p;

    if (is_not_internal_ref(ref))
	return 0;

    erts_iref_storage_save(&air->iref, ref);

    if (is_not_list(allocs))
	return 0;

    alist = allocs;

    while (is_list(alist)) {
	int saved = 0;
	Eterm* consp = list_val(alist);
	Eterm alloc = CAR(consp);

	for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++)
	    if (erts_is_atom_str(erts_alc_a2ad[ai], alloc, 0))
		goto save_alloc;
	if (erts_is_atom_str("mseg_alloc", alloc, 0)) {
	    ai = ERTS_ALC_INFO_A_MSEG_ALLOC;
	    goto save_alloc;
	}
        if (erts_is_atom_str("erts_mmap", alloc, 0)) {
            ai = ERTS_ALC_INFO_A_ERTS_MMAP;
            goto save_alloc;
        }
	if (erts_is_atom_str("alloc_util", alloc, 0)) {
	    ai = ERTS_ALC_INFO_A_ALLOC_UTIL;
	save_alloc:
	    if (req_ai[ai])
		return 0;
	    air->allocs[airix++] = ai;
	    req_ai[ai] = 1;
	    saved = 1;
	}

	if (!saved)
	    return 0;

	alist = CDR(consp);
    }

    if (is_not_nil(alist))
	return 0;

    air->allocs[airix] = ERTS_ALC_A_INVALID;

    erts_atomic32_init_nob(&air->refc,
                           (erts_aint32_t) erts_no_aux_work_threads-1);

    erts_proc_add_refc(c_p, (Sint) erts_no_aux_work_threads-1);

    if (erts_no_aux_work_threads > 2)
	erts_schedule_multi_misc_aux_work(1,
                                          1,
					  erts_no_aux_work_threads-1,
					  reply_alloc_info,
					  (void *) air);

    reply_alloc_info((void *) air);

    return 1;
}

Eterm erts_alloc_set_dyn_param(Process* c_p, Eterm tuple)
{
    ErtsAllocatorThrSpec_t *tspec;
    ErtsAlcType_t ai;
    Allctr_t* allctr;
    Eterm* tp;
    Eterm res;

    if (!is_tuple_arity(tuple, 3))
        goto badarg;

    tp = tuple_val(tuple);

    /*
     * Ex: {ets_alloc, sbct, 256000}
     */
    if (!is_atom(tp[1]) || !is_atom(tp[2]) || !is_integer(tp[3]))
        goto badarg;

    for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++)
        if (erts_is_atom_str(erts_alc_a2ad[ai], tp[1], 0))
            break;

    if (ai > ERTS_ALC_A_MAX)
        goto badarg;

    if (!erts_allctrs_info[ai].enabled ||
        !erts_allctrs_info[ai].alloc_util) {
        return am_notsup;
    }

    if (tp[2] == am_sbct) {
        Uint sbct;
        int i, ok;

        if (!term_to_Uint(tp[3], &sbct))
            goto badarg;

        tspec = &erts_allctr_thr_spec[ai];
        if (tspec->enabled) {
            ok = 0;
            for (i = 0; i < tspec->size; i++) {
                allctr = tspec->allctr[i];
                ok |= allctr->try_set_dyn_param(allctr, am_sbct, sbct);
            }
        }
        else {
            allctr = erts_allctrs_info[ai].extra;
            ok = allctr->try_set_dyn_param(allctr, am_sbct, sbct);
        }
        return ok ? am_ok : am_notsup;
    }
    return am_notsup;

badarg:
    ERTS_BIF_PREP_ERROR(res, c_p, EXC_BADARG);
    return res;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * NOTE: erts_alc_test() is only supposed to be used for testing.            *
 *                                                                           *
 * Keep alloc_SUITE_data/allocator_test.h updated if changes are made        *
 * to erts_alc_test()                                                        *
\*                                                                           */
#define ERTS_ALC_TEST_ABORT erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error\n")

UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3)
{
    switch (op >> 8) {
    case 0x0:	return erts_alcu_test(op,  a1, a2);
    case 0x1:	return erts_gfalc_test(op, a1, a2);
    case 0x2:	return erts_bfalc_test(op, a1, a2);
    case 0x3:	return erts_afalc_test(op, a1, a2);
    case 0x4:	return erts_mseg_test(op,  a1, a2, a3);
    case 0x5:	return erts_aoffalc_test(op, a1, a2);
    case 0xf:
	switch (op) {
	case 0xf00:
	    if (((Allctr_t *) a1)->thread_safe)
		return (UWord) erts_alcu_alloc_ts(ERTS_ALC_T_TEST,
							  (void *) a1,
							  (Uint) a2);
	    else
		return (UWord) erts_alcu_alloc(ERTS_ALC_T_TEST,
						       (void *) a1,
						       (Uint) a2);
	case 0xf01:
	    if (((Allctr_t *) a1)->thread_safe)
		return (UWord) erts_alcu_realloc_ts(ERTS_ALC_T_TEST,
							    (void *) a1,
							    (void *) a2,
							    (Uint) a3);
	    else
		return (UWord) erts_alcu_realloc(ERTS_ALC_T_TEST,
							 (void *) a1,
							 (void *) a2,
							 (Uint) a3);
	case 0xf02:
	    if (((Allctr_t *) a1)->thread_safe)
		erts_alcu_free_ts(ERTS_ALC_T_TEST, (void *) a1, (void *) a2);
	    else
		erts_alcu_free(ERTS_ALC_T_TEST, (void *) a1, (void *) a2);
	    return 0;
	case 0xf03: {
	    Allctr_t *allctr;
	    struct au_init init;

	    SET_DEFAULT_ALLOC_OPTS(&init);
	    init.enable = 1;
	    init.astrat = ERTS_ALC_S_GOODFIT;
	    init.init.util.name_prefix = (char *) a1;
	    init.init.util.alloc_no = ERTS_ALC_A_TEST;
	    init.init.util.alloc_strat = init.astrat;
	    init.init.util.ts = 1;
	    if ((char **) a3) {
		char **argv = (char **) a3;
		int i = 0;
		while (argv[i]) {
		    if (argv[i][0] == '-' && argv[i][1] == 't')
			handle_au_arg(&init, &argv[i][2], argv, &i, 0);
		    else
			return (UWord) NULL;
		    i++;
		}
	    }

	    switch (init.astrat) {
	    case ERTS_ALC_S_GOODFIT:
		allctr = erts_gfalc_start((GFAllctr_t *)
					  erts_alloc(ERTS_ALC_T_TEST,
						     sizeof(GFAllctr_t)),
					  &init.init.gf,
					  &init.init.util);
		break;
	    case ERTS_ALC_S_BESTFIT:
		allctr = erts_bfalc_start((BFAllctr_t *)
					  erts_alloc(ERTS_ALC_T_TEST,
						     sizeof(BFAllctr_t)),
					  &init.init.bf,
					  &init.init.util);
		break;
	    case ERTS_ALC_S_AFIT:
		allctr = erts_afalc_start((AFAllctr_t *)
					  erts_alloc(ERTS_ALC_T_TEST,
							    sizeof(AFAllctr_t)),
					  &init.init.af,
					  &init.init.util);
		break;
	    case ERTS_ALC_S_FIRSTFIT:
		allctr = erts_aoffalc_start((AOFFAllctr_t *)
					  erts_alloc(ERTS_ALC_T_TEST,
						     sizeof(AOFFAllctr_t)),
					  &init.init.aoff,
					  &init.init.util);
		break;

	    default:
		ASSERT(0);
		allctr = NULL;
		break;
	    }

	    return (UWord) allctr;
	}
	case 0xf04:
	    erts_alcu_stop((Allctr_t *) a1);
	    erts_free(ERTS_ALC_T_TEST, (void *) a1);
	    break;
	case 0xf05: return (UWord) 1;
	case 0xf06: return (UWord) ((Allctr_t *) a1)->thread_safe;
#ifdef ETHR_NO_FORKSAFETY
	case 0xf07: return (UWord) 0;
#else
	case 0xf07: return (UWord) ((Allctr_t *) a1)->thread_safe;
#endif
	case 0xf08: {
	    ethr_mutex *mtx = erts_alloc(ERTS_ALC_T_TEST, sizeof(ethr_mutex));
	    if (ethr_mutex_init(mtx) != 0)
		ERTS_ALC_TEST_ABORT;
	    return (UWord) mtx;
	}
	case 0xf09: {
	    ethr_mutex *mtx = (ethr_mutex *) a1;
	    if (ethr_mutex_destroy(mtx) != 0)
		ERTS_ALC_TEST_ABORT;
	    erts_free(ERTS_ALC_T_TEST, (void *) mtx);
	    break;
	}
	case 0xf0a:
	    ethr_mutex_lock((ethr_mutex *) a1);
	    break;
	case 0xf0b:
	    ethr_mutex_unlock((ethr_mutex *) a1);
	    break;
	case 0xf0c: {
	    ethr_cond *cnd = erts_alloc(ERTS_ALC_T_TEST, sizeof(ethr_cond));
	    if (ethr_cond_init(cnd) != 0)
		ERTS_ALC_TEST_ABORT;
	    return (UWord) cnd;
	}
	case 0xf0d: {
	    ethr_cond *cnd = (ethr_cond *) a1;
	    if (ethr_cond_destroy(cnd) != 0)
		ERTS_ALC_TEST_ABORT;
	    erts_free(ERTS_ALC_T_TEST, (void *) cnd);
	    break;
	}
	case 0xf0e:
	    ethr_cond_broadcast((ethr_cond *) a1);
	    break;
	case 0xf0f: {
	    int res;
	    do {
		res = ethr_cond_wait((ethr_cond *) a1, (ethr_mutex *) a2);
	    } while (res == EINTR);
	    break;
	}
	case 0xf10: {
	    ethr_tid *tid = erts_alloc(ERTS_ALC_T_TEST, sizeof(ethr_tid));
	    if (ethr_thr_create(tid,
				(void * (*)(void *)) a1,
				(void *) a2,
				NULL) != 0)
		ERTS_ALC_TEST_ABORT;
	    return (UWord) tid;
	}
	case 0xf11: {
	    ethr_tid *tid = (ethr_tid *) a1;
	    if (ethr_thr_join(*tid, NULL) != 0)
		ERTS_ALC_TEST_ABORT;
	    erts_free(ERTS_ALC_T_TEST, (void *) tid);
	    break;
	}
	case 0xf12:
	    ethr_thr_exit((void *) a1);
	    ERTS_ALC_TEST_ABORT;
	    break;
	case 0xf13: return (UWord) 1;
	case 0xf14: return (UWord) erts_alloc(ERTS_ALC_T_TEST, (Uint)a1);

	case 0xf15: erts_free(ERTS_ALC_T_TEST, (void*)a1); return 0;

        case 0xf16: return (UWord) erts_realloc(ERTS_ALC_T_TEST, (void*)a1, (Uint)a2);

	case 0xf17: {
            Uint extra_hdr_sz = UNIT_CEILING((Uint)a1);
	    ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST];
	    Uint offset = ts->allctr[0]->mbc_header_size;
	    void* orig_creating_mbc = ts->allctr[0]->creating_mbc;
	    void* orig_destroying_mbc = ts->allctr[0]->destroying_mbc;
	    void* new_creating_mbc = *(void**)a2; /* inout arg */
	    void* new_destroying_mbc = *(void**)a3; /* inout arg */
	    int i;

	    for (i=0; i < ts->size; i++) {
		Allctr_t* ap = ts->allctr[i];
		if (ap->mbc_header_size != offset
		    || ap->creating_mbc != orig_creating_mbc
		    || ap->destroying_mbc != orig_destroying_mbc
		    || ap->mbc_list.first != NULL)
		    return -1;
	    }
	    for (i=0; i < ts->size; i++) {
		ts->allctr[i]->mbc_header_size += extra_hdr_sz;
		ts->allctr[i]->creating_mbc = new_creating_mbc;
		ts->allctr[i]->destroying_mbc = new_destroying_mbc;
	    }
	    *(void**)a2 = orig_creating_mbc;
	    *(void**)a3 = orig_destroying_mbc;
	    return offset;
	}
	case 0xf18: {
	    ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST];
	    return ts->allctr[0]->largest_mbc_size;
	}
	default:
	    break;
	}
	return (UWord) 0;
    default:
	break;
    }

    ASSERT(0);
    return ~((UWord) 0);
}

#ifdef DEBUG
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * Debug stuff                                                               *
\*                                                                           */

#if 0
#define PRINT_OPS
#else
#undef PRINT_OPS
#endif

#ifdef HARD_DEBUG
#define FENCE_SZ		(4*sizeof(UWord))
#else
#define FENCE_SZ		(3*sizeof(UWord))
#endif

#if defined(ARCH_64)
#define FENCE_PATTERN 0xABCDEF97ABCDEF97
#else
#define FENCE_PATTERN 0xABCDEF97
#endif

#define TYPE_PATTERN_MASK  ERTS_ALC_N_MASK
#define TYPE_PATTERN_SHIFT 16

#define FIXED_FENCE_PATTERN_MASK \
  (~((UWord) (TYPE_PATTERN_MASK << TYPE_PATTERN_SHIFT)))
#define FIXED_FENCE_PATTERN \
  (FENCE_PATTERN & FIXED_FENCE_PATTERN_MASK)

#define MK_PATTERN(T) \
  (FIXED_FENCE_PATTERN | (((T) & TYPE_PATTERN_MASK) << TYPE_PATTERN_SHIFT))

#define GET_TYPE_OF_PATTERN(P) \
  (((P) >> TYPE_PATTERN_SHIFT) & TYPE_PATTERN_MASK)

#ifdef HARD_DEBUG

#define ERL_ALC_HDBG_MAX_MBLK 100000
#define ERTS_ALC_O_CHECK -1

typedef struct hdbg_mblk_ hdbg_mblk;
struct hdbg_mblk_ {
    hdbg_mblk *next;
    hdbg_mblk *prev;
    void *p;
    Uint s;
    ErtsAlcType_t n;
};

static hdbg_mblk hdbg_mblks[ERL_ALC_HDBG_MAX_MBLK];

static hdbg_mblk *free_hdbg_mblks;
static hdbg_mblk *used_hdbg_mblks;
static erts_mtx_t hdbg_mblk_mtx;

static void
hdbg_init(void)
{
    int i;
    for (i = 0; i < ERL_ALC_HDBG_MAX_MBLK-1; i++)
	hdbg_mblks[i].next = &hdbg_mblks[i+1];
    hdbg_mblks[ERL_ALC_HDBG_MAX_MBLK-1].next = NULL;
    free_hdbg_mblks = &hdbg_mblks[0];
    used_hdbg_mblks = NULL;
    erts_mtx_init(&hdbg_mblk_mtx, "erts_alloc_hard_debug", NIL,
        ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR);
}

static void *check_memory_fence(void *ptr,
				Uint *size,
				ErtsAlcType_t n,
				int func);
void erts_hdbg_chk_blks(void);

void
erts_hdbg_chk_blks(void)
{
    hdbg_mblk *mblk;

    erts_mtx_lock(&hdbg_mblk_mtx);
    for (mblk = used_hdbg_mblks; mblk; mblk = mblk->next) {
	Uint sz;
	check_memory_fence(mblk->p, &sz, mblk->n, ERTS_ALC_O_CHECK);
	ASSERT(sz == mblk->s);
    }
    erts_mtx_unlock(&hdbg_mblk_mtx);
}

static hdbg_mblk *
hdbg_alloc(void *p, Uint s, ErtsAlcType_t n)
{
    hdbg_mblk *mblk;

    erts_mtx_lock(&hdbg_mblk_mtx);
    mblk = free_hdbg_mblks;
    if (!mblk) {
	erts_fprintf(stderr,
		     "Ran out of debug blocks; please increase "
		     "ERL_ALC_HDBG_MAX_MBLK=%d and recompile!\n",
		     ERL_ALC_HDBG_MAX_MBLK);
	abort();
    }
    free_hdbg_mblks = mblk->next;

    mblk->p = p;
    mblk->s = s;
    mblk->n = n;

    mblk->next = used_hdbg_mblks;
    mblk->prev = NULL;
    if (used_hdbg_mblks)
	used_hdbg_mblks->prev = mblk;
    used_hdbg_mblks = mblk;
    erts_mtx_unlock(&hdbg_mblk_mtx);
    return (void *) mblk;
}

static void
hdbg_free(hdbg_mblk *mblk)
{
    erts_mtx_lock(&hdbg_mblk_mtx);
    if (mblk->next)
	mblk->next->prev = mblk->prev;
    if (mblk->prev)
	mblk->prev->next = mblk->next;
    else
	used_hdbg_mblks = mblk->next;

    mblk->next = free_hdbg_mblks;
    free_hdbg_mblks = mblk;
    erts_mtx_unlock(&hdbg_mblk_mtx);
}

#endif

#ifdef  ERTS_ALLOC_UTIL_HARD_DEBUG
static void *check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func);

void check_allocated_block( Uint type, void *blk)
{
    Uint dummy;
    check_memory_fence(blk, &dummy, ERTS_ALC_T2N(type), ERTS_ALC_O_FREE);
}

void check_allocators(void)
{
    int i;
    if (!erts_initialized)
	return;
    for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; ++i) {
	if (erts_allctrs_info[i].alloc_util) {
	    ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) erts_allctrs[i].extra;
	    Allctr_t *allctr = real_af->extra;
	    Carrier_t *ct;
	if (allctr->thread_safe)
	    erts_mtx_lock(&allctr->mutex);

	    if (allctr->check_mbc) {
		for (ct = allctr->mbc_list.first; ct; ct = ct->next) {
		    fprintf(stderr,"Checking allocator %d\r\n",i);
		    allctr->check_mbc(allctr,ct);
		}
	    }
	if (allctr->thread_safe)
	    erts_mtx_unlock(&allctr->mutex);
	}
    }
}
#endif

static void *
set_memory_fence(void *ptr, Uint sz, ErtsAlcType_t n)
{
    UWord *ui_ptr;
    UWord pattern;
#ifdef HARD_DEBUG
    hdbg_mblk **mblkpp;
#endif

    if (!ptr)
	return NULL;

    ui_ptr = (UWord *) ptr;
    pattern = MK_PATTERN(n);

#ifdef HARD_DEBUG
    mblkpp = (hdbg_mblk **) ui_ptr++;
#endif

    *(ui_ptr++) = sz;
    *(ui_ptr++) = pattern;
    sys_memcpy((void *) (((char *) ui_ptr)+sz), (void *) &pattern, sizeof(UWord));

#ifdef HARD_DEBUG
    *mblkpp = hdbg_alloc((void *) ui_ptr, sz, n);
#endif

    return (void *) ui_ptr;
}

static void *
check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
{
    Uint sz;
    Uint found_type;
    UWord pre_pattern, expected_pattern;
    UWord post_pattern;
    UWord *ui_ptr;
#ifdef HARD_DEBUG
    hdbg_mblk *mblk;
#endif

    if (!ptr)
	return NULL;

    expected_pattern = MK_PATTERN(n);

    ui_ptr = (UWord *) ptr;
    pre_pattern = *(--ui_ptr);
    *size = sz = *(--ui_ptr);
#ifdef HARD_DEBUG
    mblk = (hdbg_mblk *) *(--ui_ptr);
#endif

    found_type = GET_TYPE_OF_PATTERN(pre_pattern);

    if (found_type != n) {
        erts_exit(ERTS_ABORT_EXIT, "ERROR: Miss matching allocator types"
                  " used in alloc and free\n");
    }

    if (pre_pattern != expected_pattern) {
	if ((FIXED_FENCE_PATTERN_MASK & pre_pattern) != FIXED_FENCE_PATTERN)
	    erts_exit(ERTS_ABORT_EXIT,
		     "ERROR: Fence at beginning of memory block (p=0x%u) "
		     "clobbered.\n",
		     (UWord) ptr);
    }

    sys_memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord));

    if (post_pattern != expected_pattern || pre_pattern != post_pattern) {
	char fbuf[10];
	char obuf[10];
	char *ftype;
	char *otype;
	char *op_str;

	if ((FIXED_FENCE_PATTERN_MASK & post_pattern) != FIXED_FENCE_PATTERN)
	    erts_exit(ERTS_ABORT_EXIT,
		     "ERROR: Fence at end of memory block (p=0x%u, sz=%u) "
		     "clobbered.\n",
		     (UWord) ptr, (UWord) sz);
	if (found_type != GET_TYPE_OF_PATTERN(post_pattern))
	    erts_exit(ERTS_ABORT_EXIT,
		     "ERROR: Fence around memory block (p=0x%u, sz=%u) "
		     "clobbered.\n",
		     (UWord) ptr, (UWord) sz);

	ftype = type_no_str(found_type);
	if (!ftype) {
	    erts_snprintf(fbuf, sizeof(fbuf), "%d", (int) found_type);
	    ftype = fbuf;
	}
	otype = type_no_str(n);
	if (!otype) {
	    erts_snprintf(obuf, sizeof(obuf), "%d", (int) n);
	    otype = obuf;
	}

	switch (func) {
	case ERTS_ALC_O_ALLOC:		op_str = "allocated";	break;
	case ERTS_ALC_O_REALLOC:	op_str = "reallocated";	break;
	case ERTS_ALC_O_FREE:		op_str = "freed";	break;
	default:			op_str = "???";		break;
	}

	erts_exit(ERTS_ABORT_EXIT,
		 "ERROR: Memory block (p=0x%u, sz=%u) allocated as type \"%s\","
		 " but %s as type \"%s\".\n",
		 (UWord) ptr, (UWord) sz, ftype, op_str, otype);
    }

#ifdef HARD_DEBUG
    switch (func) {
    case ERTS_ALC_O_REALLOC:
    case ERTS_ALC_O_FREE:
	hdbg_free(mblk);
	break;
    default:
	break;
    }
#endif

    return (void *) ui_ptr;
}

static ErtsAllocatorFunctions_t real_allctrs[ERTS_ALC_A_MAX+1];

static void *
debug_alloc(ErtsAlcType_t type, void *extra, Uint size)
{
    ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
    ErtsAlcType_t n;
    Uint dsize;
    void *res;

#ifdef HARD_DEBUG
    erts_hdbg_chk_blks();
#endif

    n = ERTS_ALC_T2N(type);

    ASSERT(ERTS_ALC_N_MIN <= n && n <= ERTS_ALC_N_MAX);
    dsize = size + FENCE_SZ;
    res = (*real_af->alloc)(type, real_af->extra, dsize);

    res = set_memory_fence(res, size, n);

#ifdef PRINT_OPS
    fprintf(stderr, "0x%lx = alloc(%s, %lu)\r\n",
	    (Uint) res, ERTS_ALC_N2TD(n), size);
#endif

    return res;
}


static void *
debug_realloc(ErtsAlcType_t type, void *extra, void *ptr, Uint size)
{
    ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
    ErtsAlcType_t n;
    Uint dsize;
    Uint old_size;
    void *dptr;
    void *res;

    n = ERTS_ALC_T2N(type);

    ASSERT(ERTS_ALC_N_MIN <= n && n <= ERTS_ALC_N_MAX);

    dsize = size + FENCE_SZ;
    dptr = check_memory_fence(ptr, &old_size, n, ERTS_ALC_O_REALLOC);

#ifdef HARD_DEBUG
    erts_hdbg_chk_blks();
#endif

    if (ptr && old_size > size)
	sys_memset((void *) (((char *) ptr) + size),
		   0xf,
		   sizeof(Uint) + old_size - size);

    res = (*real_af->realloc)(type, real_af->extra, dptr, dsize);

    res = set_memory_fence(res, size, n);

#ifdef PRINT_OPS
    fprintf(stderr, "0x%lx = realloc(%s, 0x%lx, %lu)\r\n",
	    (Uint) res, ERTS_ALC_N2TD(n), (Uint) ptr, size);
#endif

    return res;
}

static void
debug_free(ErtsAlcType_t type, void *extra, void *ptr)
{
    ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra;
    ErtsAlcType_t n;
    void *dptr;
    Uint size;
    int free_pattern;

    n = ERTS_ALC_T2N(type);
    free_pattern = n;

    ASSERT(ERTS_ALC_N_MIN <= n && n <= ERTS_ALC_N_MAX);

    if (!ptr)
        return;

    dptr = check_memory_fence(ptr, &size, n, ERTS_ALC_O_FREE);

    sys_memset((void *) dptr, free_pattern, size + FENCE_SZ);

    (*real_af->free)(type, real_af->extra, dptr);

#ifdef PRINT_OPS
    fprintf(stderr, "free(%s, 0x%lx)\r\n", ERTS_ALC_N2TD(n), (Uint) ptr);
#endif

#ifdef HARD_DEBUG
    erts_hdbg_chk_blks();
#endif
}

static Uint
install_debug_functions(void)
{
    int i;
    ERTS_CT_ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs));

    sys_memcpy((void *)real_allctrs,(void *)erts_allctrs,sizeof(erts_allctrs));

    for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) {
	erts_allctrs[i].alloc	= debug_alloc;
	erts_allctrs[i].realloc	= debug_realloc;
	erts_allctrs[i].free	= debug_free;
	erts_allctrs[i].extra	= (void *) &real_allctrs[i];
    }
    return FENCE_SZ;
}

#endif /* #ifdef DEBUG */
