/*
** Copyright (C) 1994-2000,2002, 2004, 2006, 2008 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/

/*
** mercury_memory.h:
** general memory-allocation related stuff for the Mercury runtime.
**
** This defines the different memory areas used by the Mercury runtime,
** including the det & nondet stacks, the heap (and solutions heap),
** and the fake_reg array for holding Mercury virtual registers.
** It also provides interfaces for constructing new memory zones,
** and for allocating (possibly shared) memory.
*/

#ifndef	MERCURY_MEMORY_H
#define	MERCURY_MEMORY_H

#include "mercury_memory_zones.h"

#include <stddef.h>		/* for size_t */

#include "mercury_types.h"	/* for MR_Word */
#include "mercury_std.h"	/* for MR_bool */
#include "mercury_conf.h"	/* for MR_CONSERVATIVE_GC, etc. */

#if defined(MR_CONSERVATIVE_GC)
  #if defined(MR_MPS_GC)
    #include "mercury_mps.h"	/* for GC_FREE */
  #endif
  #if defined(MR_BOEHM_GC)
    #define GC_I_HIDE_POINTERS
    #include "gc.h"		/* for GC_FREE */
  #endif
#endif

/*
** MR_round_up(amount, align) returns `amount' rounded up to the nearest
** alignment boundary.  `align' must be a power of 2.
*/

#define MR_round_up(amount, align)	((((amount) - 1) | ((align) - 1)) + 1)

/*
** MR_kilobytes_to_bytes_and_round_up(var) takes an original value in var
** which represents an memory size measured in kilobytes, and converts it to
** a memory size measured in bytes, and then rounds it up to be the next
** multiple of MR_unit.
*/

#define MR_kilobytes_to_bytes_and_round_up(var)				\
	do {								\
		var = MR_round_up(var * 1024, MR_unit);			\
	} while (0)

/*
** For these functions, see the comments in mercury_memory.c and
** mercury_engine.c
*/

extern	void	MR_init_memory(void);
extern	void	MR_init_heap(void);

#ifdef MR_CONSERVATIVE_GC
  extern void	MR_init_conservative_GC(void);
#endif

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

/*
** MR_malloc() and MR_realloc() are like the standard C malloc() and realloc()
** functions, except that the return values are checked.
**
** Structures allocated with MR_malloc() and MR_realloc() must NOT contain
** pointers into GC'ed memory, because those pointers will never be traced
** by the conservative GC. Use MR_GC_malloc() or MR_GC_malloc_uncollectable()
** for that.
**
** MR_NEW(type):
**	Allocates space for an object of the specified type.
**
** MR_NEW_ARRAY(type, num):
**	Allocates space for an array of objects of the specified type.
**
** MR_RESIZE_ARRAY(ptr, type, num):
**	Resizes the array, as with realloc().
**
** MR_malloc(bytes):
**	Allocates the given number of bytes.
**
** MR_realloc(old, bytes):
**	Allocates the given number of bytes, copies over the old contents of
**	the previously allocated block pointed to by old, and then frees that
**	old block.
**
** MR_ensure_big_enough_buffer(buffer_ptr, buffer_size_ptr, needed_size):
**	Given a character buffer pointed to by buffer_ptr whose is given by
**	*buffer_size_ptr, ensure that the buffer is big enough to hold
**	needed_size characters. If it needs to make the block bigger,
**	this function will update both *buffer_ptr and *buffer_size_ptr.
**
** MR_free(ptr):
**	Deallocates the memory.
*/

extern	void	*MR_malloc(size_t n);
extern	void	*MR_realloc(void *old, size_t n);
extern	void 	MR_ensure_big_enough_buffer(char **buffer_ptr,
			int *buffer_size_ptr, int needed_size);


#define MR_free(ptr) free(ptr)
#define MR_free_func free

#define MR_NEW(type) \
	((type *) MR_malloc(sizeof(type)))

#define MR_NEW_ARRAY(type, num) \
	((type *) MR_malloc((num) * sizeof(type)))

#define MR_RESIZE_ARRAY(ptr, type, num) \
	((type *) MR_realloc((ptr), (num) * sizeof(type)))

/*
** These routines all allocate memory that will be traced by the
** conservative garbage collector, if conservative GC is enabled.
** (For the native GC, you need to call MR_add_root() to register roots.)
** These routines all check for a null return value themselves,
** so the caller need not check.
**
** MR_GC_NEW(type):
**	Allocates space for an object of the specified type.
**	If conservative GC is enabled, the object will be garbage collected
**	when it is no longer referenced from GC-traced memory.
**	Memory allocated with malloc() (or MR_malloc() or MR_NEW())
**	is not GC-traced.  Nor is thread-local storage.
**
** MR_GC_NEW_UNCOLLECTABLE(type):
**	Allocates space for an object of the specified type.
**	The object will not be garbage collected even if it is not referenced,
**	or only referenced from thread-local storage or storage allocated
**	with malloc(). It should be explicitly deallocated with MR_GC_free().
**
** MR_GC_NEW_ARRAY(type, num):
**	Allocates space for an array of objects of the specified type.
**
** MR_GC_RESIZE_ARRAY(ptr, type, num):
**	Resizes the array, as with realloc().
**
** MR_GC_malloc(bytes):
**	Allocates the given number of bytes.
**	If conservative GC is enabled, the memory will be garbage collected
**	when it is no longer referenced from GC-traced memory (see above).
**
** MR_GC_malloc_uncollectable(bytes):
**	Allocates the given number of bytes.
**	The memory will not be garbage collected, and so
**	it should be explicitly deallocated using MR_GC_free().
**
** MR_GC_realloc(ptr, bytes):
**	Reallocates the memory block pointed to by ptr.
**
** MR_GC_free(ptr):
**	Deallocates the memory.
**
** MR_GC_register_finalizer(ptr, finalize_func, data):
**	When ptr is garbage collected invoke (*finalize_func)(ptr, data).
**	ptr must have be a pointer to space allocated by the garbage collector.
**	data is a pointer to some user-defined data.
**	XXX currently this only works with the Boehm collector, i.e. in .gc
**          grades, it is a no-op in non .gc grades.
**
**      XXX this interface is subject to change.
**
** Note: consider using the _attrib variants below.
*/

extern	void	*MR_GC_malloc(size_t num_bytes);
extern	void	*MR_GC_malloc_uncollectable(size_t num_bytes);
extern	void	*MR_GC_realloc(void *ptr, size_t num_bytes);

typedef void 	(*MR_GC_finalizer)(void *ptr, void *data);

#define MR_GC_NEW(type) \
	((type *) MR_GC_malloc(sizeof(type)))

#define MR_GC_NEW_UNCOLLECTABLE(type) \
	((type *) MR_GC_malloc_uncollectable(sizeof(type)))

#define MR_GC_NEW_ARRAY(type, num) \
	((type *) MR_GC_malloc((num) * sizeof(type)))

#define MR_GC_RESIZE_ARRAY(ptr, type, num) \
	((type *) MR_GC_realloc((ptr), (num) * sizeof(type)))

#ifdef MR_CONSERVATIVE_GC
  #define MR_GC_free(ptr) GC_FREE(ptr)
#else
  #define MR_GC_free(ptr) free(ptr)
#endif

#if defined(MR_CONSERVATIVE_GC) && defined(MR_BOEHM_GC)
  #define MR_GC_register_finalizer(ptr, finalizer, data) \
  	GC_REGISTER_FINALIZER((ptr), (finalizer), (data), 0, 0)
#else
  #define MR_GC_register_finalizer(ptr, finalizer, data)
#endif

/*
** MR_GC_NEW_ATTRIB(type, attrib):
** MR_GC_NEW_UNCOLLECTABLE_ATTRIB(type, attrib):
** MR_GC_NEW_ARRAY_ATTRIB(type, attrib):
** MR_GC_malloc_attrib(bytes, attrib):
** MR_GC_malloc_uncollectable_attrib(bytes, attrib):
** MR_GC_realloc_attrib(ptr, num_bytes):
**	In grades with memory attribution support, these variants will allocate
**	an extra word before the object.  The value stored `attrib' is stored
**	in that extra word.
**
** MR_GC_RESIZE_ARRAY_ATTRIB(ptr, type, num):
** MR_GC_free_attrib(ptr):
**	These variants take into account the extra word before ptr.
**	You must NOT pass pointers which were returned by non-"attrib"
**	functions/macros to these "attrib" variants, and vice versa.
*/

#define MR_GC_NEW_ATTRIB(type, attrib) \
	((type *) MR_GC_malloc_attrib(sizeof(type), (attrib)))

#define MR_GC_NEW_UNCOLLECTABLE_ATTRIB(type, attrib) \
	((type *) MR_GC_malloc_uncollectable_attrib(sizeof(type), (attrib)))

#define MR_GC_NEW_ARRAY_ATTRIB(type, num, attrib) \
	((type *) MR_GC_malloc_attrib((num) * sizeof(type), (attrib)))

#define MR_GC_RESIZE_ARRAY_ATTRIB(ptr, type, num) \
	((type *) MR_GC_realloc_attrib((ptr), (num) * sizeof(type)))

extern	void	*MR_GC_malloc_attrib(size_t num_bytes, void *attrib);
extern	void	*MR_GC_malloc_uncollectable_attrib(size_t num_bytes,
		    void *attrib);
extern	void	*MR_GC_realloc_attrib(void *ptr, size_t num_bytes);
extern	void	MR_GC_free_attrib(void *ptr);

struct MR_AllocSiteInfo_Struct {
    MR_Code	*MR_asi_proc;
    const char  *MR_asi_file_name;
    const int   MR_asi_line_number;
    const char  *MR_asi_type;
    const int   MR_asi_words;
};

/*
** Built-in allocation site ids for use in the runtime and other
** hand-written code when context-specific ids are unavailable.
** MR_ALLOC_SITE_RUNTIME is a catch-all for internal runtime structures;
** these are hidden by default in `mprof -s' output.
*/

#define MR_ALLOC_SITE_NONE		((void *) 0)
#ifdef MR_MPROF_PROFILE_MEMORY_ATTRIBUTION
    /* These must match the entries in mercury_heap_profile.c. */
    extern MR_AllocSiteInfo		MR_builtin_alloc_sites[7];
    #define MR_ALLOC_SITE_RUNTIME	((void *) &MR_builtin_alloc_sites[0])
    #define MR_ALLOC_SITE_FLOAT		((void *) &MR_builtin_alloc_sites[1])
    #define MR_ALLOC_SITE_STRING	((void *) &MR_builtin_alloc_sites[2])
    #define MR_ALLOC_SITE_TYPE_INFO	((void *) &MR_builtin_alloc_sites[3])
    #define MR_ALLOC_SITE_FOREIGN	((void *) &MR_builtin_alloc_sites[4])
    #define MR_ALLOC_SITE_TABLING	((void *) &MR_builtin_alloc_sites[5])
    #define MR_ALLOC_SITE_STM		((void *) &MR_builtin_alloc_sites[6])
#else
    #define MR_ALLOC_ID			MR_ALLOC_SITE_NONE
    #define MR_ALLOC_SITE_RUNTIME	MR_ALLOC_SITE_NONE
    #define MR_ALLOC_SITE_FLOAT		MR_ALLOC_SITE_NONE
    #define MR_ALLOC_SITE_STRING	MR_ALLOC_SITE_NONE
    #define MR_ALLOC_SITE_TYPE_INFO	MR_ALLOC_SITE_NONE
    #define MR_ALLOC_SITE_FOREIGN	MR_ALLOC_SITE_NONE
    #define MR_ALLOC_SITE_TABLING	MR_ALLOC_SITE_NONE
    #define MR_ALLOC_SITE_STM		MR_ALLOC_SITE_NONE
#endif

extern	void	*MR_new_object_func(size_t num_bytes,
		    MR_AllocSiteInfoPtr alloc_id, const char *name);
extern	void	*MR_new_object_atomic_func(size_t num_bytes,
		    MR_AllocSiteInfoPtr alloc_id, const char *name);

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

/*
** MR_copy_string makes a copy of the given string,
** using memory allocated with MR_malloc().
*/

extern char	*MR_copy_string(const char *s);

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

/*
** `MR_unit' is the size of the minimum unit of memory we allocate (in bytes).
** `MR_page_size' is the size of a single page of memory.
*/

extern	size_t          MR_unit;
extern	size_t          MR_page_size;

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

/*
** Users need to call MR_add_root() for any global variable which contains
** pointers to the Mercury heap. This information is only used for agc grades.
*/
#ifdef MR_NATIVE_GC
  #define MR_add_root(root_ptr, type_info) 				\
	MR_agc_add_root((root_ptr), (type_info))
#else
  #define MR_add_root(root_ptr, type_info) /* nothing */
#endif

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

#endif /* not MERCURY_MEMORY_H */
