text: /*
   * %CopyrightBegin%
   *
   * Copyright Ericsson AB 1996-2018. 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%
   */
  
  #ifndef __GLOBAL_H__
  #define __GLOBAL_H__
  
  #include "sys.h"
  #include <stddef.h> /* offsetof() */
  #include "erl_alloc.h"
  #include "erl_vm.h"
  #include "erl_node_container_utils.h"
  #include "hash.h"
  #include "index.h"
  #include "atom.h"
  #include "code_ix.h"
  #include "export.h"
  #include "module.h"
  #include "register.h"
  #include "erl_fun.h"
  #include "erl_node_tables.h"
  #include "erl_process.h"
  #include "erl_sys_driver.h"
  #include "erl_debug.h"
  #include "error.h"
  #include "erl_utils.h"
  #include "erl_port.h"
  #include "erl_gc.h"
  #include "erl_nif.h"
  #define ERTS_BINARY_TYPES_ONLY__
  #include "erl_binary.h"
  #undef ERTS_BINARY_TYPES_ONLY__
  
  struct enif_func_t;
  
  #ifdef  DEBUG
  #  define ERTS_NIF_ASSERT_IN_ENV
  #endif
  struct enif_environment_t /* ErlNifEnv */
  {
      struct erl_module_nif* mod_nif;
      Process* proc;
      Eterm* hp;
      Eterm* hp_end;
      ErlHeapFragment* heap_frag;
      int fpe_was_unmasked;
      struct enif_tmp_obj_t* tmp_obj_list;
      int exception_thrown; /* boolean */
      Process *tracee;
      int exiting; /* boolean (dirty nifs might return in exiting state) */
  
  #ifdef ERTS_NIF_ASSERT_IN_ENV
      int dbg_disable_assert_in_env;
  #endif
  };
  struct enif_resource_type_t
  {
      struct enif_resource_type_t* next;   /* list of all resource types */
      struct enif_resource_type_t* prev;
      struct erl_module_nif* owner;  /* that created this type and thus implements the destructor*/
      ErlNifResourceDtor* dtor;      /* user destructor function */
      ErlNifResourceStop* stop;
      ErlNifResourceDown* down;
      erts_refc_t refc;  /* num of resources of this type (HOTSPOT warning)
                            +1 for active erl_module_nif */
      Eterm module;
      Eterm name;
  };
  
  typedef struct
  {
      erts_mtx_t lock;
      ErtsMonitor* root;
      Uint refc;
      size_t user_data_sz;
  } ErtsResourceMonitors;
  
  typedef struct ErtsResource_
  {
      struct enif_resource_type_t* type;
      ErtsResourceMonitors* monitors;
  #ifdef DEBUG
      erts_refc_t nif_refc;
  #else
  # ifdef ARCH_32
      byte align__[4];
  # endif
  #endif
      char data[1];
  }ErtsResource;
  
  #define DATA_TO_RESOURCE(PTR) ErtsContainerStruct(PTR, ErtsResource, data)
  #define erts_resource_ref_size(P) ERTS_MAGIC_REF_THING_SIZE
  
  extern Eterm erts_bld_resource_ref(Eterm** hp, ErlOffHeap*, ErtsResource*);
  
  extern void erts_pre_nif(struct enif_environment_t*, Process*,
  			 struct erl_module_nif*, Process* tracee);
  extern void erts_post_nif(struct enif_environment_t* env);
  extern void erts_resource_stop(ErtsResource*, ErlNifEvent, int is_direct_call);
  void erts_fire_nif_monitor(ErtsMonitor *tmon);
  void erts_nif_demonitored(ErtsResource* resource);
  extern void erts_add_taint(Eterm mod_atom);
  extern Eterm erts_nif_taints(Process* p);
  extern void erts_print_nif_taints(fmtfn_t to, void* to_arg);
  void erts_unload_nif(struct erl_module_nif* nif);
  extern void erl_nif_init(void);
  extern int erts_nif_get_funcs(struct erl_module_nif*,
                                struct enif_func_t **funcs);
  extern Module *erts_nif_get_module(struct erl_module_nif*);
  extern Eterm erts_nif_call_function(Process *p, Process *tracee,
                                      struct erl_module_nif*,
                                      struct enif_func_t *,
                                      int argc, Eterm *argv);
  
  int erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p,
  			BeamInstr *I, Eterm *reg);
  
  
  /* Driver handle (wrapper for old plain handle) */
  #define ERL_DE_OK      0
  #define ERL_DE_UNLOAD  1
  #define ERL_DE_FORCE_UNLOAD 2 
  #define ERL_DE_RELOAD  3
  #define ERL_DE_FORCE_RELOAD  4
  #define ERL_DE_PERMANENT 5
  
  #define ERL_DE_PROC_LOADED 0
  #define ERL_DE_PROC_AWAIT_UNLOAD 1
  #define ERL_DE_PROC_AWAIT_UNLOAD_ONLY 2
  #define ERL_DE_PROC_AWAIT_LOAD 3
  
  /* Flags for process entries */
  #define ERL_DE_FL_DEREFERENCED 1
  
  /* Flags for drivers, put locking policy here /PaN */
  #define ERL_DE_FL_KILL_PORTS 1
  
  #define ERL_FL_CONSISTENT_MASK ( ERL_DE_FL_KILL_PORTS )
  
  /* System specific load errors are returned as positive values */
  #define ERL_DE_NO_ERROR 0
  #define ERL_DE_LOAD_ERROR_NO_INIT -1
  #define ERL_DE_LOAD_ERROR_FAILED_INIT -2
  #define ERL_DE_LOAD_ERROR_BAD_NAME -3
  #define ERL_DE_LOAD_ERROR_NAME_TO_LONG -4
  #define ERL_DE_LOAD_ERROR_INCORRECT_VERSION -5
  #define ERL_DE_ERROR_NO_DDLL_FUNCTIONALITY -6
  #define ERL_DE_ERROR_UNSPECIFIED -7
  #define ERL_DE_LOOKUP_ERROR_NOT_FOUND -8
  #define ERL_DE_DYNAMIC_ERROR_OFFSET -10
  
  typedef struct de_proc_entry {
      Process *proc;                   /* The process... */
      Uint    awaiting_status;         /* PROC_LOADED == Have loaded the driver
  			                PROC_AWAIT_UNLOAD == Wants to be notified 
  			                when we have unloaded the driver (was locked)
  			                PROC_AWAIT_LOAD == Wants to be notified when we
  			                reloaded the driver (old was locked) */
      Uint    flags;                   /* ERL_FL_DE_DEREFERENCED when reload in progress */
      Eterm   heap[ERTS_REF_THING_SIZE];    /* "ref heap" */
      struct  de_proc_entry *next;
  } DE_ProcEntry;
  
  typedef struct {
      void         *handle;             /* Handle for DLL or SO (for dyn. drivers). */
      DE_ProcEntry *procs;              /* List of pids that have loaded this driver,
  				         or that wait for it to change state */
      erts_refc_t  refc;                /* Number of ports/processes having
  					 references to the driver */
      erts_atomic32_t port_count;   /* Number of ports using the driver */
      Uint         flags;               /* ERL_DE_FL_KILL_PORTS */
      int          status;              /* ERL_DE_xxx */
      char         *full_path;          /* Full path of the driver */
      char         *reload_full_path;   /* If status == ERL_DE_RELOAD, this contains
  				         full name of driver (path) */
      char         *reload_driver_name; /* ... and this contains the driver name */
      Uint         reload_flags;        /* flags for reloaded driver */
  } DE_Handle;
  
  /*
   * This structure represents a link to the next driver.
   */
  
  struct erts_driver_t_ {
      erts_driver_t *next;
      erts_driver_t *prev;
      Eterm name_atom;
      char *name;
      struct {
  	int major;
  	int minor;
      } version;
      int flags;
      DE_Handle *handle;
      erts_mtx_t *lock;
      ErlDrvEntry *entry;
      ErlDrvData (*start)(ErlDrvPort port, char *command, SysDriverOpts* opts);
      void (*stop)(ErlDrvData drv_data);
      void (*finish)(void);
      void (*flush)(ErlDrvData drv_data);
      void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len);
      void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev); /* Might be NULL */
      ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command,
  			    char *buf, ErlDrvSizeT len,
  			    char **rbuf, ErlDrvSizeT rlen); /* Might be NULL */
      ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command,
  			 char *buf, ErlDrvSizeT len,
  			 char **rbuf, ErlDrvSizeT rlen, /* Might be NULL */
  			 unsigned int *flags);
      void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event); 
      void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event);  
      void (*timeout)(ErlDrvData drv_data);
      void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data); /* Might be NULL */ 
      void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor);
      void (*stop_select)(ErlDrvEvent event, void*); /* Might be NULL */
      void (*emergency_close)(ErlDrvData drv_data);  /* Might be NULL */
  };
  
  extern erts_driver_t *driver_list;
  extern erts_rwmtx_t erts_driver_list_lock;
  
  extern void erts_ddll_init(void);
  extern void erts_ddll_lock_driver(DE_Handle *dh, char *name);
  
  /* These are for bookkeeping */
  extern void erts_ddll_increment_port_count(DE_Handle *dh);
  extern void erts_ddll_decrement_port_count(DE_Handle *dh);
  
  /* These makes things happen, drivers may be scheduled for unload etc */
  extern void erts_ddll_reference_driver(DE_Handle *dh);
  extern void erts_ddll_reference_referenced_driver(DE_Handle *dh);
  extern void erts_ddll_dereference_driver(DE_Handle *dh);
  
  extern char *erts_ddll_error(int code);
  extern void erts_ddll_proc_dead(Process *p, ErtsProcLocks plocks);
  extern int erts_ddll_driver_ok(DE_Handle *dh);
  extern void erts_ddll_remove_monitor(Process *p,
  				     Eterm ref,
  				     ErtsProcLocks plocks);
  extern Eterm erts_ddll_monitor_driver(Process *p,
  				      Eterm description,
  				      ErtsProcLocks plocks);
  
  /*
   * This structure represents one type of a binary in a process.
   */
  
  typedef struct proc_bin {
      Eterm thing_word;		/* Subtag REFC_BINARY_SUBTAG. */
      Uint size;			/* Binary size in bytes. */
      struct erl_off_heap_header *next;
      Binary *val;		/* Pointer to Binary structure. */
      byte *bytes;		/* Pointer to the actual data bytes. */
      Uint flags;			/* Flag word. */
  } ProcBin;
  
  #define PB_IS_WRITABLE 1	/* Writable (only one reference to ProcBin) */
  #define PB_ACTIVE_WRITER 2	/* There is an active writer */
  
  /*
   * ProcBin size in Eterm words.
   */
  #define PROC_BIN_SIZE (sizeof(ProcBin)/sizeof(Eterm))
  
  union erl_off_heap_ptr {
      struct erl_off_heap_header* hdr;
      ProcBin *pb;
      struct erl_fun_thing* fun;
      struct external_thing_* ext;
      ErtsMRefThing *mref;
      Eterm* ep;
      void* voidp;
  };
  
  /* controls warning mapping in error_logger */
  
  extern Eterm node_cookie;
  extern Uint display_items;	/* no of items to display in traces etc */
  
  extern int erts_backtrace_depth;
  extern erts_atomic32_t erts_max_gen_gcs;
  
  extern int bif_reductions;      /* reductions + fcalls (when doing call_bif) */
  extern int stackdump_on_exit;
  
  /*
   * Here is an implementation of a lightweight stack.
   *
   * Use it like this:
   *
   * DECLARE_ESTACK(Stack)	(At the start of a block)
   * ...
   * ESTACK_PUSH(Stack, Term)
   * ...
   * if (ESTACK_ISEMPTY(Stack)) {
   *    Stack is empty
   * } else {
   *    Term = ESTACK_POP(Stack);
   *    Process popped Term here
   * }
   * ...
   * DESTROY_ESTACK(Stack)
   */
  
  typedef struct ErtsEStack_ {
      Eterm* start;
      Eterm* sp;
      Eterm* end;
      Eterm* edefault;
      ErtsAlcType_t alloc_type;
  }ErtsEStack;
  
  #define DEF_ESTACK_SIZE (16)
  
  void erl_grow_estack(ErtsEStack*, Uint need);
  #define ESTK_CONCAT(a,b) a##b
  #define ESTK_DEF_STACK(s) ESTK_CONCAT(s,_default_estack)
  
  #define DECLARE_ESTACK(s)				\
      Eterm ESTK_DEF_STACK(s)[DEF_ESTACK_SIZE];		\
      ErtsEStack s = {					\
          ESTK_DEF_STACK(s),  /* start */ 		\
          ESTK_DEF_STACK(s),  /* sp */			\
          ESTK_DEF_STACK(s) + DEF_ESTACK_SIZE, /* end */	\
          ESTK_DEF_STACK(s),  /* default */		\
          ERTS_ALC_T_ESTACK /* alloc_type */		\
      }
  
  #define ESTACK_CHANGE_ALLOCATOR(s,t)					\
  do {									\
      if ((s).start != ESTK_DEF_STACK(s)) {				\
  	erts_exit(ERTS_ERROR_EXIT, "Internal error - trying to change allocator "	\
  		 "type of active estack\n");				\
      }									\
      (s).alloc_type = (t);						\
   } while (0)
  
  #define DESTROY_ESTACK(s)				\
  do {							\
      if ((s).start != ESTK_DEF_STACK(s)) {		\
  	erts_free((s).alloc_type, (s).start); 		\
      }							\
  } while(0)
  
  
  /*
   * Do not free the stack after this, it may have pointers into what
   * was saved in 'dst'.
   */
  #define ESTACK_SAVE(s,dst)\
  do {\
      if ((s).start == ESTK_DEF_STACK(s)) {\
  	UWord _wsz = ESTACK_COUNT(s);\
  	(dst)->start = erts_alloc((s).alloc_type,\
  				  DEF_ESTACK_SIZE * sizeof(Eterm));\
  	sys_memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\
  	(dst)->sp = (dst)->start + _wsz;\
  	(dst)->end = (dst)->start + DEF_ESTACK_SIZE;\
          (dst)->edefault = NULL;\
  	(dst)->alloc_type = (s).alloc_type;\
      } else\
          *(dst) = (s);\
   } while (0)
  
  #define DESTROY_SAVED_ESTACK(estack)\
  do {\
      if ((estack)->start) {\
  	erts_free((estack)->alloc_type, (estack)->start);\
  	(estack)->start = NULL;\
      }\
  } while(0)
  
  #define CLEAR_SAVED_ESTACK(estack) ((void) ((estack)->start = NULL))
  
  /*
   * Use on empty stack, only the allocator can be changed before this.
   * The src stack is reset to NULL.
   */
  #define ESTACK_RESTORE(s, src)			\
  do {						\
      ASSERT((s).start == ESTK_DEF_STACK(s));	\
      (s) = *(src);  /* struct copy */		\
      (src)->start = NULL;			\
      ASSERT((s).sp >= (s).start);		\
      ASSERT((s).sp <= (s).end);			\
  } while (0)
  
  #define ESTACK_IS_STATIC(s) ((s).start == ESTK_DEF_STACK(s))
  
  #define ESTACK_PUSH(s, x)			\
  do {						\
      if ((s).sp == (s).end) {			\
  	erl_grow_estack(&(s), 1); 		\
      }						\
      *(s).sp++ = (x);				\
  } while(0)
  
  #define ESTACK_PUSH2(s, x, y)			\
  do {						\
      if ((s).sp > (s).end - 2) {			\
  	erl_grow_estack(&(s), 2);		\
      }						\
      *(s).sp++ = (x);				\
      *(s).sp++ = (y);				\
  } while(0)
  
  #define ESTACK_PUSH3(s, x, y, z)		\
  do {						\
      if ((s).sp > (s).end - 3) {			\
  	erl_grow_estack(&s, 3); 		\
      }						\
      *(s).sp++ = (x);				\
      *(s).sp++ = (y);				\
      *(s).sp++ = (z);				\
  } while(0)
  
  #define ESTACK_PUSH4(s, E1, E2, E3, E4)		\
  do {						\
      if ((s).sp > (s).end - 4) {			\
  	erl_grow_estack(&s, 4);                 \
      }						\
      *(s).sp++ = (E1);				\
      *(s).sp++ = (E2);				\
      *(s).sp++ = (E3);				\
      *(s).sp++ = (E4);				\
  } while(0)
  
  #define ESTACK_RESERVE(s, push_cnt)             \
  do {					        \
      if ((s).sp > (s).end - (push_cnt)) {	\
  	erl_grow_estack(&(s), (push_cnt));	\
      }					        \
  } while(0)
  
  /* Must be preceded by ESTACK_RESERVE */
  #define ESTACK_FAST_PUSH(s, x)				\
  do {							\
      ASSERT((s).sp < (s).end);                           \
      *s.sp++ = (x);					\
  } while(0)
  
  #define ESTACK_COUNT(s) ((s).sp - (s).start)
  #define ESTACK_ISEMPTY(s) ((s).sp == (s).start)
  #define ESTACK_POP(s) (*(--(s).sp))
  
  
  /*
   * WSTACK: same as ESTACK but with UWord instead of Eterm
   */
  
  typedef struct ErtsWStack_ {
      UWord* wstart;
      UWord* wsp;
      UWord* wend;
      UWord* wdefault;
      ErtsAlcType_t alloc_type;
  }ErtsWStack;
  
  #define DEF_WSTACK_SIZE (16)
  
  void erl_grow_wstack(ErtsWStack*, Uint need);
  #define WSTK_CONCAT(a,b) a##b
  #define WSTK_DEF_STACK(s) WSTK_CONCAT(s,_default_wstack)
  
  #define WSTACK_DECLARE(s)				\
      UWord WSTK_DEF_STACK(s)[DEF_WSTACK_SIZE];		\
      ErtsWStack s = {					\
          WSTK_DEF_STACK(s),  /* wstart */ 		\
          WSTK_DEF_STACK(s),  /* wsp */			\
          WSTK_DEF_STACK(s) + DEF_WSTACK_SIZE, /* wend */	\
          WSTK_DEF_STACK(s),  /* wdflt */ 		\
          ERTS_ALC_T_ESTACK /* alloc_type */		\
      }
  #define DECLARE_WSTACK WSTACK_DECLARE
  
  typedef struct ErtsDynamicWStack_ {
      UWord default_stack[DEF_WSTACK_SIZE];
      ErtsWStack ws;
  }ErtsDynamicWStack;
  
  #define WSTACK_INIT(dwsp, ALC_TYPE)                               \
  do {	 	                                                  \
      (dwsp)->ws.wstart   = (dwsp)->default_stack;                  \
      (dwsp)->ws.wsp      = (dwsp)->default_stack;                  \
      (dwsp)->ws.wend     = (dwsp)->default_stack + DEF_WSTACK_SIZE;\
      (dwsp)->ws.wdefault = (dwsp)->default_stack;                  \
      (dwsp)->ws.alloc_type = ALC_TYPE;                             \
  } while (0)
  
  #define WSTACK_CHANGE_ALLOCATOR(s,t)					\
  do {									\
      if (s.wstart != WSTK_DEF_STACK(s)) {				\
  	erts_exit(ERTS_ERROR_EXIT, "Internal error - trying to change allocator "	\
  		 "type of active wstack\n");				\
      }									\
      s.alloc_type = (t);							\
   } while (0)
  
  #define WSTACK_DESTROY(s)				\
  do {							\
      if (s.wstart != s.wdefault) {		        \
  	erts_free(s.alloc_type, s.wstart); 		\
      }							\
  } while(0)
  #define DESTROY_WSTACK WSTACK_DESTROY
  
  #define WSTACK_DEBUG(s) \
      do { \
  	fprintf(stderr, "wstack size   = %ld\r\n", s.wsp - s.wstart); \
  	fprintf(stderr, "wstack wstart = %p\r\n", s.wstart); \
  	fprintf(stderr, "wstack wsp    = %p\r\n", s.wsp); \
      } while(0)
  
  /*
   * Do not free the stack after this, it may have pointers into what
   * was saved in 'dst'.
   */
  #define WSTACK_SAVE(s,dst)\
  do {\
      if (s.wstart == WSTK_DEF_STACK(s)) {\
  	UWord _wsz = WSTACK_COUNT(s);\
  	(dst)->wstart = erts_alloc(s.alloc_type,\
  				  DEF_WSTACK_SIZE * sizeof(UWord));\
  	sys_memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\
  	(dst)->wsp = (dst)->wstart + _wsz;\
  	(dst)->wend = (dst)->wstart + DEF_WSTACK_SIZE;\
          (dst)->wdefault = NULL;\
  	(dst)->alloc_type = s.alloc_type;\
      } else\
          *(dst) = s;\
   } while (0)
  
  #define DESTROY_SAVED_WSTACK(wstack)\
  do {\
      if ((wstack)->wstart) {\
  	erts_free((wstack)->alloc_type, (wstack)->wstart);\
  	(wstack)->wstart = NULL;\
      }\
  } while(0)
  
  #define CLEAR_SAVED_WSTACK(wstack) ((void) ((wstack)->wstart = NULL))
  
  /*
   * Use on empty stack, only the allocator can be changed before this.
   * The src stack is reset to NULL.
   */
  #define WSTACK_RESTORE(s, src)			\
  do {						\
      ASSERT(s.wstart == WSTK_DEF_STACK(s));	\
      s = *(src);  /* struct copy */		\
      (src)->wstart = NULL;			\
      ASSERT(s.wsp >= s.wstart);			\
      ASSERT(s.wsp <= s.wend);			\
  } while (0)
  
  #define WSTACK_IS_STATIC(s) (s.wstart == WSTK_DEF_STACK(s))
  
  #define WSTACK_PUSH(s, x)				\
  do {							\
      if (s.wsp == s.wend) {				\
  	erl_grow_wstack(&s, 1); 	                \
      }							\
      *s.wsp++ = (x);					\
  } while(0)
  
  #define WSTACK_PUSH2(s, x, y)			\
  do {						\
      if (s.wsp > s.wend - 2) {			\
  	erl_grow_wstack(&s, 2);                 \
      }						\
      *s.wsp++ = (x);				\
      *s.wsp++ = (y);				\
  } while(0)
  
  #define WSTACK_PUSH3(s, x, y, z)		\
  do {						\
      if (s.wsp > s.wend - 3) {	                \
  	erl_grow_wstack(&s, 3);                 \
      }						\
      *s.wsp++ = (x);				\
      *s.wsp++ = (y);				\
      *s.wsp++ = (z);				\
  } while(0)
  
  #define WSTACK_PUSH4(s, A1, A2, A3, A4)		\
  do {						\
      if (s.wsp > s.wend - 4) {	                \
  	erl_grow_wstack(&s, 4);                 \
      }						\
      *s.wsp++ = (A1);				\
      *s.wsp++ = (A2);				\
      *s.wsp++ = (A3);				\
      *s.wsp++ = (A4);				\
  } while(0)
  
  #define WSTACK_PUSH5(s, A1, A2, A3, A4, A5)     \
  do {						\
      if (s.wsp > s.wend - 5) {	                \
  	erl_grow_wstack(&s, 5);                 \
      }						\
      *s.wsp++ = (A1);				\
      *s.wsp++ = (A2);				\
      *s.wsp++ = (A3);				\
      *s.wsp++ = (A4);				\
      *s.wsp++ = (A5);				\
  } while(0)
  
  #define WSTACK_PUSH6(s, A1, A2, A3, A4, A5, A6) \
  do {						\
      if (s.wsp > s.wend - 6) {	                \
  	erl_grow_wstack(&s, 6);                 \
      }						\
      *s.wsp++ = (A1);				\
      *s.wsp++ = (A2);				\
      *s.wsp++ = (A3);				\
      *s.wsp++ = (A4);				\
      *s.wsp++ = (A5);				\
      *s.wsp++ = (A6);				\
  } while(0)
  
  #define WSTACK_RESERVE(s, push_cnt)             \
  do {						\
      if (s.wsp > s.wend - (push_cnt)) { 	        \
  	erl_grow_wstack(&s, (push_cnt));        \
      }                                           \
  } while(0)
  
  /* Must be preceded by WSTACK_RESERVE */
  #define WSTACK_FAST_PUSH(s, x)                  \
  do {						\
      ASSERT(s.wsp < s.wend);                     \
      *s.wsp++ = (x);                             \
  } while(0)
  
  #define WSTACK_COUNT(s) (s.wsp - s.wstart)
  #define WSTACK_ISEMPTY(s) (s.wsp == s.wstart)
  #define WSTACK_POP(s) ((ASSERT(s.wsp > s.wstart)),*(--s.wsp))
  
  #define WSTACK_ROLLBACK(s, count) (ASSERT(WSTACK_COUNT(s) >= (count)), \
                                     s.wsp = s.wstart + (count))
  
  /* PSTACK - Stack of any type.
   * Usage:
   * {
   * #define PSTACK_TYPE MyType
   *    PSTACK_DECLARE(s,16);
   *    MyType *sp = PSTACK_PUSH(s);
   *
   *    sp->x = ....
   *    sp->y = ....
   *    sp = PSTACK_PUSH(s);
   *    ...
   *    sp = PSTACK_POP(s);
   *    if (PSTACK_IS_EMPTY(s)) {
   *        // sp is invalid when stack is empty after pop
   *    }
   *
   *    PSTACK_DESTROY(s);
   * }
   */
  
  
  typedef struct ErtsPStack_ {
      byte* pstart;
      int offs;   /* "stack pointer" as byte offset from pstart */
      int size;   /* allocated size in bytes */
      ErtsAlcType_t alloc_type;
  }ErtsPStack;
  
  void erl_grow_pstack(ErtsPStack* s, void* default_pstack, unsigned need_bytes);
  #define PSTK_CONCAT(a,b) a##b
  #define PSTK_DEF_STACK(s) PSTK_CONCAT(s,_default_pstack)
  
  #define PSTACK_DECLARE(s, DEF_PSTACK_SIZE) \
  PSTACK_TYPE PSTK_DEF_STACK(s)[DEF_PSTACK_SIZE];                            \
  ErtsPStack s = { (byte*)PSTK_DEF_STACK(s), /* pstart */                    \
                   -(int)sizeof(PSTACK_TYPE), /* offs */                     \
                   DEF_PSTACK_SIZE*sizeof(PSTACK_TYPE), /* size */           \
                   ERTS_ALC_T_ESTACK   /* alloc_type */                      \
  }
  
  #define PSTACK_CHANGE_ALLOCATOR(s,t)					\
  do {									\
      if (s.pstart != (byte*)PSTK_DEF_STACK(s)) {				\
  	erts_exit(ERTS_ERROR_EXIT, "Internal error - trying to change allocator "	\
  		 "type of active pstack\n");				\
      }									\
      s.alloc_type = (t);							\
   } while (0)
  
  #define PSTACK_DESTROY(s)				\
  do {							\
      if (s.pstart != (byte*)PSTK_DEF_STACK(s)) {		\
  	erts_free(s.alloc_type, s.pstart); 		\
      }							\
  } while(0)
  
  #define PSTACK_IS_EMPTY(s) (s.offs < 0)
  
  #define PSTACK_COUNT(s) ((s.offs + sizeof(PSTACK_TYPE)) / sizeof(PSTACK_TYPE))
  
  #define PSTACK_TOP(s) (ASSERT(!PSTACK_IS_EMPTY(s)), \
                         (PSTACK_TYPE*)(s.pstart + s.offs))
  
  #define PSTACK_PUSH(s) 		                                            \
      (s.offs += sizeof(PSTACK_TYPE),                                         \
       ((s.offs == s.size) ? erl_grow_pstack(&s, PSTK_DEF_STACK(s),           \
                                            sizeof(PSTACK_TYPE)) : (void)0),  \
       ((PSTACK_TYPE*) (s.pstart + s.offs)))
  
  #define PSTACK_POP(s) ((s.offs -= sizeof(PSTACK_TYPE)), \
                         (PSTACK_TYPE*)(s.pstart + s.offs))
  
  /*
   * Do not free the stack after this, it may have pointers into what
   * was saved in 'dst'.
   */
  #define PSTACK_SAVE(s,dst)\
  do {\
      if (s.pstart == (byte*)PSTK_DEF_STACK(s)) {\
  	UWord _pbytes = PSTACK_COUNT(s) * sizeof(PSTACK_TYPE);\
  	(dst)->pstart = erts_alloc(s.alloc_type,\
  				   sizeof(PSTK_DEF_STACK(s)));\
  	sys_memcpy((dst)->pstart, s.pstart, _pbytes);\
  	(dst)->offs = s.offs;\
  	(dst)->size = s.size;\
  	(dst)->alloc_type = s.alloc_type;\
      } else\
          *(dst) = s;\
   } while (0)
  
  /*
   * Use on empty stack, only the allocator can be changed before this.
   * The src stack is reset to NULL.
   */
  #define PSTACK_RESTORE(s, src)			        \
  do {						        \
      ASSERT(s.pstart == (byte*)PSTK_DEF_STACK(s));	\
      s = *(src);  /* struct copy */		        \
      (src)->pstart = NULL;			        \
      ASSERT(s.offs >= -(int)sizeof(PSTACK_TYPE));        \
      ASSERT(s.offs < s.size);			        \
  } while (0)
  
  #define PSTACK_DESTROY_SAVED(pstack)\
  do {\
      if ((pstack)->pstart) {\
  	erts_free((pstack)->alloc_type, (pstack)->pstart);\
  	(pstack)->pstart = NULL;\
      }\
  } while(0)
  
  
  /*
   *  An implementation of lightweight unbounded queues,
   *  using a circular dynamic array.
   *  It does not include support for change_allocator.
   *
   *  Use it like this:
   *
   *  DECLARE_EQUEUE(Queue)	(At the start of a block)
   *  ...
   *  EQUEUE_PUT(Queue, Term)
   *  ...
   *  if (EQUEUE_ISEMPTY(Queue)) {
   *     Queue is empty
   *  } else {
   *     Term = EQUEUE_GET(Stack);
   *     Process popped Term here
   *  }
   *  ...
   *  DESTROY_EQUEUE(Queue)
   */
  
  typedef struct {
      Eterm* start;
      Eterm* front;
      Eterm* back;
      int possibly_empty;
      Eterm* end;
      ErtsAlcType_t alloc_type;
  } ErtsEQueue;
  
  #define DEF_EQUEUE_SIZE (16)
  
  void erl_grow_equeue(ErtsEQueue*, Eterm* def_queue);
  #define EQUE_CONCAT(a,b) a##b
  #define EQUE_DEF_QUEUE(q) EQUE_CONCAT(q,_default_equeue)
  
  #define DECLARE_EQUEUE(q)				\
      UWord EQUE_DEF_QUEUE(q)[DEF_EQUEUE_SIZE];     	\
      ErtsEQueue q = {					\
          EQUE_DEF_QUEUE(q), /* start */			\
          EQUE_DEF_QUEUE(q), /* front */			\
          EQUE_DEF_QUEUE(q), /* back */			\
          1,                 /* possibly_empty */		\
          EQUE_DEF_QUEUE(q) + DEF_EQUEUE_SIZE, /* end */	\
          ERTS_ALC_T_ESTACK  /* alloc_type */		\
      }
  
  #define DESTROY_EQUEUE(q)				\
  do {							\
      if (q.start != EQUE_DEF_QUEUE(q)) {			\
        erts_free(q.alloc_type, q.start);			\
      }							\
  } while(0)
  
  #define EQUEUE_PUT_UNCHECKED(q, x)			\
  do {							\
      q.possibly_empty = 0;				\
      *(q.back) = (x);                    		\
      if (++(q.back) == q.end) {				\
  	q.back = q.start;				\
      }							\
  } while(0)
  
  #define EQUEUE_PUT(q, x)				\
  do {							\
      if (q.back == q.front && !q.possibly_empty) {	\
          erl_grow_equeue(&q, EQUE_DEF_QUEUE(q));		\
      }							\
      EQUEUE_PUT_UNCHECKED(q, x);				\
  } while(0)
  
  #define EQUEUE_ISEMPTY(q) (q.back == q.front && q.possibly_empty)
  
  ERTS_GLB_INLINE Eterm erts_equeue_get(ErtsEQueue *q);
  
  #if ERTS_GLB_INLINE_INCL_FUNC_DEF
  ERTS_GLB_INLINE Eterm erts_equeue_get(ErtsEQueue *q) {
      Eterm x;
      q->possibly_empty = 1;
      x = *(q->front);
      if (++(q->front) == q->end) {
          q->front = q->start;
      }
      return x;
  }
  #endif
  #define EQUEUE_GET(q) erts_equeue_get(&(q));
  
  /* binary.c */
  
  void erts_emasculate_writable_binary(ProcBin* pb);
  Eterm erts_new_heap_binary(Process *p, byte *buf, int len, byte** datap);
  Eterm erts_new_mso_binary(Process*, byte*, Uint);
  Eterm new_binary(Process*, byte*, Uint);
  Eterm erts_heap_factory_new_binary(ErtsHeapFactory *hfact, byte *buf,
                                     Uint len, Uint reserve_size);
  Eterm erts_realloc_binary(Eterm bin, size_t size);
  Eterm erts_build_proc_bin(ErlOffHeap*, Eterm*, Binary*);
  
  /* erl_bif_info.c */
  
  Eterm
  erts_bld_port_info(Eterm **hpp,
  		   ErlOffHeap *ohp,
  		   Uint *szp,
  		   Port *prt,
  		   Eterm item); 
  
  void erts_bif_info_init(void);
  
  /* bif.c */
  
  void erts_queue_monitor_message(Process *,
  				ErtsProcLocks*,
  				Eterm,
  				Eterm,
  				Eterm,
  				Eterm);
  void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
  			   Eterm (*bif)(Process*, Eterm*, BeamInstr*));
  void erts_init_bif(void);
  Eterm erl_send(Process *p, Eterm to, Eterm msg);
  int erts_set_group_leader(Process *proc, Eterm new_gl);
  
  /* erl_bif_op.c */
  
  Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2);
  
  /* beam_bif_load.c */
  Eterm erts_check_process_code(Process *c_p, Eterm module, int *redsp, int fcalls);
  Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed);
  
  typedef struct ErtsLiteralArea_ {
      struct erl_off_heap_header *off_heap;
      Eterm *end;
      Eterm start[1]; /* beginning of area */
  } ErtsLiteralArea;
  
  void erts_queue_release_literals(Process *c_p, ErtsLiteralArea* literals);
  
  #define ERTS_LITERAL_AREA_ALLOC_SIZE(N) \
      (sizeof(ErtsLiteralArea) + sizeof(Eterm)*((N) - 1))
  
  extern erts_atomic_t erts_copy_literal_area__;
  #define ERTS_COPY_LITERAL_AREA()					\
      ((ErtsLiteralArea *) erts_atomic_read_nob(&erts_copy_literal_area__))
  extern Process *erts_literal_area_collector;
  
  extern Process *erts_code_purger;
  
  /* beam_load.c */
  typedef struct {
      ErtsCodeMFA* mfa;		/* Pointer to: Mod, Name, Arity */
      Uint needed;		/* Heap space needed for entire tuple */
      Uint32 loc;			/* Location in source code */
      Eterm* fname_ptr;		/* Pointer to fname table */
  } FunctionInfo;
  
  Binary* erts_alloc_loader_state(void);
  Eterm erts_module_for_prepared_code(Binary* magic);
  Eterm erts_has_code_on_load(Binary* magic);
  Eterm erts_prepare_loading(Binary* loader_state,  Process *c_p,
  			   Eterm group_leader, Eterm* modp,
  			   byte* code, Uint size);
  Eterm erts_finish_loading(Binary* loader_state, Process* c_p,
  			  ErtsProcLocks c_p_locks, Eterm* modp);
  Eterm erts_preload_module(Process *c_p, ErtsProcLocks c_p_locks,
  			  Eterm group_leader, Eterm* mod, byte* code, Uint size);
  void init_load(void);
  ErtsCodeMFA* find_function_from_pc(BeamInstr* pc);
  Eterm* erts_build_mfa_item(FunctionInfo* fi, Eterm* hp,
  			   Eterm args, Eterm* mfa_p);
  void erts_set_current_function(FunctionInfo* fi, ErtsCodeMFA* mfa);
  Eterm erts_module_info_0(Process* p, Eterm module);
  Eterm erts_module_info_1(Process* p, Eterm module, Eterm what);
  Eterm erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info);
  int erts_commit_hipe_patch_load(Eterm hipe_magic_bin);
  
  /* beam_ranges.c */
  void erts_init_ranges(void);
  void erts_start_staging_ranges(int num_new);
  void erts_end_staging_ranges(int commit);
  void erts_update_ranges(BeamInstr* code, Uint size);
  void erts_remove_from_ranges(BeamInstr* code);
  UWord erts_ranges_sz(void);
  void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info);
  extern ErtsLiteralArea** erts_dump_lit_areas;
  extern Uint erts_dump_num_lit_areas;
  
  /* break.c */
  void init_break_handler(void);
  void erts_set_ignore_break(void);
  void erts_replace_intr(void);
  void process_info(fmtfn_t, void *);
  void print_process_info(fmtfn_t, void *, Process*);
  void info(fmtfn_t, void *);
  void loaded(fmtfn_t, void *);
  void erts_print_base64(fmtfn_t to, void *to_arg, byte* src, Uint size);
  
  /* sighandler sys.c */
  int erts_set_signal(Eterm signal, Eterm type);
  
  /* erl_arith.c */
  double erts_get_positive_zero_float(void);
  
  /* config.c */
  
  __decl_noreturn void __noreturn erts_exit_epilogue(void);
  __decl_noreturn void __noreturn erts_exit(int n, char*, ...);
  __decl_noreturn void __noreturn erts_flush_async_exit(int n, char*, ...);
  void erl_error(char*, va_list);
  
  /* This controls whether sharing-preserving copy is used by Erlang */
  
  #ifdef SHCOPY
  #define SHCOPY_SEND
  #define SHCOPY_SPAWN
  #endif
  
  /* The persistent state while the sharing-preserving copier works */
  
  typedef struct {
      Eterm  queue_default[DEF_EQUEUE_SIZE];
      Eterm* queue_start;
      Eterm* queue_end;
      ErtsAlcType_t queue_alloc_type;
      UWord  bitstore_default[DEF_WSTACK_SIZE];
      UWord* bitstore_start;
      ErtsAlcType_t bitstore_alloc_type;
      Eterm  shtable_default[DEF_ESTACK_SIZE];
      Eterm* shtable_start;
      ErtsAlcType_t shtable_alloc_type;
      Uint literal_size;
      Eterm *lit_purge_ptr;
      Uint lit_purge_sz;
      int copy_literals;
  } erts_shcopy_t;
  
  #define INITIALIZE_SHCOPY(info)						\
      do {								\
  	ErtsLiteralArea *larea__ = ERTS_COPY_LITERAL_AREA();		\
  	info.queue_start = info.queue_default;				\
  	info.bitstore_start = info.bitstore_default;			\
  	info.shtable_start = info.shtable_default;			\
  	info.literal_size = 0;						\
  	info.copy_literals = 0;						\
  	if (larea__) {							\
  	    info.lit_purge_ptr = &larea__->start[0];			\
  	    info.lit_purge_sz = larea__->end - info.lit_purge_ptr;	\
  	}								\
  	else {								\
  	    info.lit_purge_ptr = NULL;					\
  	    info.lit_purge_sz = 0;					\
  	}								\
      } while(0)
  
  #define DESTROY_SHCOPY(info)                                            \
  do {                                                                    \
      if (info.queue_start != info.queue_default) {                       \
          erts_free(info.queue_alloc_type, info.queue_start);             \
      }                                                                   \
      if (info.bitstore_start != info.bitstore_default) {                 \
          erts_free(info.bitstore_alloc_type, info.bitstore_start);       \
      }                                                                   \
      if (info.shtable_start != info.shtable_default) {                   \
          erts_free(info.shtable_alloc_type, info.shtable_start);         \
      }                                                                   \
  } while(0)
  
  /* copy.c */
  typedef struct {
      Eterm *lit_purge_ptr;
      Uint lit_purge_sz;
  } erts_literal_area_t;
  
  #define INITIALIZE_LITERAL_PURGE_AREA(Area)				\
      do {								\
  	ErtsLiteralArea *larea__ = ERTS_COPY_LITERAL_AREA();		\
  	if (larea__) {							\
  	    (Area).lit_purge_ptr = &larea__->start[0];			\
  	    (Area).lit_purge_sz = larea__->end - (Area).lit_purge_ptr;	\
  	}								\
  	else {								\
  	    (Area).lit_purge_ptr = NULL;				\
  	    (Area).lit_purge_sz = 0;					\
  	}								\
      } while(0)
  
  Eterm copy_object_x(Eterm, Process*, Uint);
  #define copy_object(Term, Proc) copy_object_x(Term,Proc,0)
  
  Uint size_object_x(Eterm, erts_literal_area_t*);
  #define size_object(Term) size_object_x(Term,NULL)
  #define size_object_litopt(Term,LitArea) size_object_x(Term,LitArea)
  
  Uint copy_shared_calculate(Eterm, erts_shcopy_t*);
  Eterm copy_shared_perform(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*);
  
  Uint size_shared(Eterm);
  
  Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint*, erts_literal_area_t*);
  #define copy_struct(Obj,Sz,HPP,OH) \
      copy_struct_x(Obj,Sz,HPP,OH,NULL,NULL)
  #define copy_struct_litopt(Obj,Sz,HPP,OH,LitArea) \
      copy_struct_x(Obj,Sz,HPP,OH,NULL,LitArea)
  
  Eterm copy_shallow(Eterm* ERTS_RESTRICT, Uint, Eterm**, ErlOffHeap*);
  
  void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first,
  			   Eterm* refs, unsigned nrefs, int literals);
  
  /* Utilities */
  void erts_monitor_nodes_delete(ErtsMonitor *);
  extern Eterm erts_monitor_nodes(Process *, Eterm, Eterm);
  extern Eterm erts_processes_monitoring_nodes(Process *);
  extern int erts_do_net_exits(DistEntry*, Eterm);
  extern int distribution_info(fmtfn_t, void *);
  extern int is_node_name_atom(Eterm a);
  
  extern int erts_net_message(Port *, DistEntry *, Uint32 conn_id,
  			    byte *, ErlDrvSizeT, byte *, ErlDrvSizeT);
  
  extern void init_dist(void);
  extern int stop_dist(void);
  
  void erl_progressf(char* format, ...);
  
  #ifdef MESS_DEBUG
  void print_pass_through(int, byte*, int);
  #endif
  
  /* beam_emu.c */
  int catchlevel(Process*);
  void init_emulator(void);
  void process_main(Eterm* x_reg_array, FloatDef* f_reg_array);
  void erts_dirty_process_main(ErtsSchedulerData *);
  Eterm build_stacktrace(Process* c_p, Eterm exc);
  Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value);
  void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth);
  
  /* erl_init.c */
  
  typedef struct {
      Eterm delay_time;
      int context_reds;
  } ErtsModifiedTimings;
  
  extern Export *erts_delay_trap;
  extern int erts_modified_timing_level;
  extern ErtsModifiedTimings erts_modified_timings[];
  #define ERTS_USE_MODIFIED_TIMING() \
    (erts_modified_timing_level >= 0)
  #define ERTS_MODIFIED_TIMING_DELAY \
    (erts_modified_timings[erts_modified_timing_level].delay_time)
  #define ERTS_MODIFIED_TIMING_CONTEXT_REDS \
    (erts_modified_timings[erts_modified_timing_level].context_reds)
  #define ERTS_MODIFIED_TIMING_INPUT_REDS \
    (erts_modified_timings[erts_modified_timing_level].input_reds)
  
  extern int erts_no_line_info;
  extern Eterm erts_error_logger_warnings;
  extern int erts_initialized;
  extern int erts_compat_rel;
  void erl_start(int, char**);
  void erts_usage(void);
  Eterm erts_preloaded(Process* p);
  
  
  /* erl_md5.c */
  
  typedef struct {
      Uint32 state[4];		/* state (ABCD) */
      Uint32 count[2];		/* number of bits, modulo 2^64 (lsb first) */
      unsigned char buffer[64];	/* input buffer */
  } MD5_CTX;
  
  void MD5Init(MD5_CTX *);
  void MD5Update(MD5_CTX *, unsigned char *, unsigned int);
  void MD5Final(unsigned char [16], MD5_CTX *);
  
  
  /* io.c */
  
  typedef struct {
      char *name;
      char *driver_name;
  } ErtsPortNames;
  
  #define ERTS_SPAWN_DRIVER 1
  #define ERTS_SPAWN_EXECUTABLE 2
  #define ERTS_SPAWN_ANY (ERTS_SPAWN_DRIVER | ERTS_SPAWN_EXECUTABLE)
  int erts_add_driver_entry(ErlDrvEntry *drv, DE_Handle *handle, int driver_list_locked, int taint);
  void erts_destroy_driver(erts_driver_t *drv);
  int erts_save_suspend_process_on_port(Port*, Process*);
  Port *erts_open_driver(erts_driver_t*, Eterm, char*, SysDriverOpts*, int *, int *);
  void erts_init_io(int, int, int);
  void erts_raw_port_command(Port*, byte*, Uint);
  void driver_report_exit(ErlDrvPort, int);
  LineBuf* allocate_linebuf(int);
  int async_ready(Port *, void*);
  ErtsPortNames *erts_get_port_names(Eterm, ErlDrvPort);
  void erts_free_port_names(ErtsPortNames *);
  Uint erts_port_ioq_size(Port *pp);
  void erts_stale_drv_select(Eterm, ErlDrvPort, ErlDrvEvent, int, int);
  
  Port *erts_get_heart_port(void);
  void erts_emergency_close_ports(void);
  void erts_ref_to_driver_monitor(Eterm ref, ErlDrvMonitor *mon);
  Eterm erts_driver_monitor_to_ref(Eterm* hp, const ErlDrvMonitor *mon);
  
  #if defined(ERTS_ENABLE_LOCK_COUNT)
  void erts_lcnt_update_driver_locks(int enable);
  void erts_lcnt_update_port_locks(int enable);
  #endif
  
  /* driver_tab.c */
  typedef struct {
      ErlDrvEntry* de;
      int taint;
  } ErtsStaticDriver;
  typedef void *(*ErtsStaticNifInitFPtr)(void);
  typedef struct ErtsStaticNifEntry_ {
      const char *nif_name;
      ErtsStaticNifInitFPtr nif_init;
      int taint;
  } ErtsStaticNifEntry;
  ErtsStaticNifEntry* erts_static_nif_get_nif_init(const char *name, int len);
  int erts_is_static_nif(void *handle);
  void erts_init_static_drivers(void);
  
  /* erl_drv_thread.c */
  void erl_drv_thr_init(void);
  
  /* utils.c */
  void erts_cleanup_offheap(ErlOffHeap *offheap);
  
  Uint64 erts_timestamp_millis(void);
  
  Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex);
  
  void *erts_calc_stacklimit(char *prev_c, UWord stacksize);
  int erts_check_below_limit(char *ptr, char *limit);
  int erts_check_above_limit(char *ptr, char *limit);
  void *erts_ptr_id(void *ptr);
  
  Eterm store_external_or_ref_in_proc_(Process *, Eterm);
  Eterm store_external_or_ref_(Uint **, ErlOffHeap*, Eterm);
  
  #define NC_HEAP_SIZE(NC) \
   (ASSERT(is_node_container((NC))), \
    IS_CONST((NC)) ? 0 : (thing_arityval(*boxed_val((NC))) + 1))
  #define STORE_NC(Hpp, ETpp, NC) \
   (ASSERT(is_node_container((NC))), \
    IS_CONST((NC)) ? (NC) : store_external_or_ref_((Hpp), (ETpp), (NC)))
  #define STORE_NC_IN_PROC(Pp, NC) \
   (ASSERT(is_node_container((NC))), \
    IS_CONST((NC)) ? (NC) : store_external_or_ref_in_proc_((Pp), (NC)))
  
  /* duplicates from big.h */
  int term_to_Uint(Eterm term, Uint *up);
  int term_to_UWord(Eterm, UWord*);
  
  #ifdef HAVE_ERTS_NOW_CPU
  extern int erts_cpu_timestamp;
  #endif
  /* erl_bif_chksum.c */
  void erts_init_bif_chksum(void);
  /* erl_bif_re.c */
  void erts_init_bif_re(void);
  Sint erts_re_set_loop_limit(Sint limit);
  /* erl_bif_binary.c */
  void erts_init_bif_binary(void);
  Sint erts_binary_set_loop_limit(Sint limit);
  
  /* erl_bif_persistent.c */
  void erts_init_bif_persistent_term(void);
  Uint erts_persistent_term_count(void);
  void erts_init_persistent_dumping(void);
  extern ErtsLiteralArea** erts_persistent_areas;
  extern Uint erts_num_persistent_areas;
  
  /* external.c */
  void erts_init_external(void);
  
  /* erl_map.c */
  void erts_init_map(void);
  
  /* beam_debug.c */
  UWord erts_check_stack_recursion_downwards(char *start_c);
  UWord erts_check_stack_recursion_upwards(char *start_c);
  int erts_is_above_stack_limit(char *ptr);
  
  /* erl_unicode.c */
  void erts_init_unicode(void);
  Sint erts_unicode_set_loop_limit(Sint limit);
  
  void erts_native_filename_put(Eterm ioterm, int encoding, byte *p) ;
  Sint erts_native_filename_need(Eterm ioterm, int encoding);
  void erts_copy_utf8_to_utf16_little(byte *target, byte *bytes, int num_chars);
  int erts_analyze_utf8(byte *source, Uint size, 
  			byte **err_pos, Uint *num_chars, int *left);
  int erts_analyze_utf8_x(byte *source, Uint size, 
  			byte **err_pos, Uint *num_chars, int *left,
  			Sint *num_latin1_chars, Uint max_chars);
  char *erts_convert_filename_to_native(Eterm name, char *statbuf, 
  				      size_t statbuf_size, 
  				      ErtsAlcType_t alloc_type, 
  				      int allow_empty, int allow_atom,
  				      Sint *used /* out */);
  char *erts_convert_filename_to_encoding(Eterm name, char *statbuf,
  					size_t statbuf_size,
  					ErtsAlcType_t alloc_type,
  					int allow_empty, int allow_atom,
  					int encoding,
  					Sint *used /* out */,
  					Uint extra);
  char* erts_convert_filename_to_wchar(byte* bytes, Uint size,
                                       char *statbuf, size_t statbuf_size,
                                       ErtsAlcType_t alloc_type, Sint* used,
                                       Uint extra_wchars);
  Eterm erts_convert_native_to_filename(Process *p, size_t size, byte *bytes);
  Eterm erts_utf8_to_list(Process *p, Uint num, byte *bytes, Uint sz, Uint left,
  			Uint *num_built, Uint *num_eaten, Eterm tail);
  int erts_utf8_to_latin1(byte* dest, const byte* source, int slen);
  #define ERTS_UTF8_OK 0
  #define ERTS_UTF8_INCOMPLETE 1
  #define ERTS_UTF8_ERROR 2
  #define ERTS_UTF8_ANALYZE_MORE 3
  #define ERTS_UTF8_OK_MAX_CHARS 4
  
  void bin_write(fmtfn_t, void*, byte*, size_t);
  Sint intlist_to_buf(Eterm, char*, Sint); /* most callers pass plain char*'s */
  int erts_unicode_list_to_buf(Eterm list, byte *buf, Sint len, Sint* written);
  Sint erts_unicode_list_to_buf_len(Eterm list);
  
  struct Sint_buf {
  #if defined(ARCH_64)
      char s[22];
  #else
      char s[12];
  #endif
  };	
  char* Sint_to_buf(Sint, struct Sint_buf*);
  
  #define ERTS_IOLIST_STATE_INITER(C_P, OBJ)	\
      {(C_P), 0, 0, (OBJ), {NULL, NULL, NULL, ERTS_ALC_T_INVALID}, 0, 0}
  
  #define ERTS_IOLIST_STATE_MOVE(TO, FROM)	\
      sys_memcpy((void *) (TO), (void *) (FROM), sizeof(ErtsIOListState))
  
  #define ERTS_IOLIST_SIZE_YIELDS_COUNT_PER_RED 8
  
  typedef struct {
      Process *c_p;
      ErlDrvSizeT size;
      Uint offs;
      Eterm obj;
      ErtsEStack estack;
      int reds_left;
      int have_size;
  } ErtsIOListState;
  
  #define ERTS_IOLIST2BUF_STATE_INITER(C_P, OBJ)	\
      {ERTS_IOLIST_STATE_INITER((C_P), (OBJ)), {NULL, 0, 0, 0}, NULL, 0, NULL, 0}
  
  #define ERTS_IOLIST2BUF_STATE_MOVE(TO, FROM)	\
      sys_memcpy((void *) (TO), (void *) (FROM), sizeof(ErtsIOList2BufState))
  
  #define ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT 32
  #define ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED 8
  #define ERTS_IOLIST_TO_BUF_BYTES_PER_RED \
      (ERTS_IOLIST_TO_BUF_YIELD_COUNT_PER_RED*ERTS_IOLIST_TO_BUF_BYTES_PER_YIELD_COUNT)
  
  typedef struct {
      ErtsIOListState iolist;
      struct {
  	byte *bptr;
  	size_t size;
  	Uint bitoffs;
  	Uint bitsize;
      } bcopy;
      char *buf;
      ErlDrvSizeT len;
      Eterm *objp;
      int offset;
  } ErtsIOList2BufState;
  
  #define ERTS_IOLIST_OK 0
  #define ERTS_IOLIST_OVERFLOW 1
  #define ERTS_IOLIST_TYPE 2
  #define ERTS_IOLIST_YIELD 3
  
  Eterm buf_to_intlist(Eterm**, const char*, size_t, Eterm); /* most callers pass plain char*'s */
  
  #define ERTS_IOLIST_TO_BUF_OVERFLOW	(~((ErlDrvSizeT) 0))
  #define ERTS_IOLIST_TO_BUF_TYPE_ERROR	(~((ErlDrvSizeT) 1))
  #define ERTS_IOLIST_TO_BUF_YIELD	(~((ErlDrvSizeT) 2))
  #define ERTS_IOLIST_TO_BUF_FAILED(R) \
      (((R) & (~((ErlDrvSizeT) 3))) == (~((ErlDrvSizeT) 3)))
  #define ERTS_IOLIST_TO_BUF_SUCCEEDED(R) \
      (!ERTS_IOLIST_TO_BUF_FAILED((R)))
  
  ErlDrvSizeT erts_iolist_to_buf(Eterm, char*, ErlDrvSizeT);
  ErlDrvSizeT erts_iolist_to_buf_yielding(ErtsIOList2BufState *);
  int erts_iolist_size_yielding(ErtsIOListState *state);
  int erts_iolist_size(Eterm, ErlDrvSizeT *);
  Sint is_string(Eterm);
  void erl_at_exit(void (*) (void*), void*);
  Eterm collect_memory(Process *);
  void dump_memory_to_fd(int);
  int dump_memory_data(const char *);
  
  Eterm erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_mixed_times(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_mixed_div(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_int_div(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_int_rem(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_band(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_bor(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_bxor(Process* p, Eterm arg1, Eterm arg2);
  Eterm erts_bnot(Process* p, Eterm arg);
  
  Eterm erts_gc_mixed_plus(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_mixed_minus(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_mixed_times(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_mixed_div(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_int_div(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_int_rem(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_band(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_bor(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_bxor(Process* p, Eterm* reg, Uint live);
  Eterm erts_gc_bnot(Process* p, Eterm* reg, Uint live);
  
  Uint erts_current_reductions(Process* current, Process *p);
  
  int erts_print_system_version(fmtfn_t to, void *arg, Process *c_p);
  
  int erts_hibernate(Process* c_p, Eterm* reg);
  
  ERTS_GLB_FORCE_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr);
  
  #if ERTS_GLB_INLINE_INCL_FUNC_DEF
  
  ERTS_GLB_FORCE_INLINE int erts_is_literal(Eterm tptr, Eterm *ptr)
  {
      ASSERT(is_boxed(tptr) || is_list(tptr));
      ASSERT(ptr == ptr_val(tptr));
  
  #if defined(ERTS_HAVE_IS_IN_LITERAL_RANGE)
      return erts_is_in_literal_range(ptr);
  #elif defined(TAG_LITERAL_PTR)
      return is_literal_ptr(tptr);
  #else
  #  error Not able to detect literals...
  #endif
  
  }
  
  #endif
  
  Eterm erts_msacc_request(Process *c_p, int action, Eterm *threads);
  
  /*
  ** Call_trace uses this API for the parameter matching functions
  */
  
  #define MatchSetRef(MPSP) 			\
  do {						\
      if ((MPSP) != NULL) {			\
  	erts_refc_inc(&(MPSP)->intern.refc, 1);	\
      }						\
  } while (0)
  
  #define MatchSetUnref(MPSP)					\
  do {								\
      if (((MPSP) != NULL)) {                                     \
  	erts_bin_release(MPSP);					\
      }								\
  } while(0)
  
  #define MatchSetGetSource(MPSP) erts_match_set_get_source(MPSP)
  
  extern Binary *erts_match_set_compile(Process *p, Eterm matchexpr, Eterm MFA);
  extern void erts_match_set_release_result(Process* p);
  ERTS_GLB_INLINE void erts_match_set_release_result_trace(Process* p, Eterm);
  
  #if ERTS_GLB_INLINE_INCL_FUNC_DEF
  ERTS_GLB_INLINE
  void erts_match_set_release_result_trace(Process* p, Eterm pam_result)
  {
      if (is_not_immed(pam_result))
          erts_match_set_release_result(p);
  }
  #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
  
  enum erts_pam_run_flags {
      ERTS_PAM_TMP_RESULT=1,
      ERTS_PAM_COPY_RESULT=2,
      ERTS_PAM_CONTIGUOUS_TUPLE=4,
      ERTS_PAM_IGNORE_TRACE_SILENT=8
  };
  extern Eterm erts_match_set_run_trace(Process *p,
                                        Process *self,
                                        Binary *mpsp,
                                        Eterm *args, int num_args,
                                        enum erts_pam_run_flags in_flags,
                                        Uint32 *return_flags);
  extern Eterm erts_match_set_get_source(Binary *mpsp);
  extern void erts_match_prog_foreach_offheap(Binary *b,
  					    void (*)(ErlOffHeap *, void *),
  					    void *);
  
  #define MATCH_SET_RETURN_TRACE    (0x1) /* return trace requested */
  #define MATCH_SET_RETURN_TO_TRACE (0x2) /* Misleading name, it is not actually
  					   set by the match program, but by the
  					   breakpoint functions */
  #define MATCH_SET_EXCEPTION_TRACE (0x4) /* exception trace requested */
  #define MATCH_SET_RX_TRACE (MATCH_SET_RETURN_TRACE|MATCH_SET_EXCEPTION_TRACE)
  
  extern erts_driver_t vanilla_driver;
  extern erts_driver_t spawn_driver;
  extern erts_driver_t forker_driver;
  extern erts_driver_t fd_driver;
  
  int erts_beam_jump_table(void);
  
  #define DeclareTmpHeap(VariableName,Size,Process) \
       Eterm VariableName[Size]
  #define DeclareTypedTmpHeap(Type,VariableName,Process)	\
       Type VariableName[1]
  #define DeclareTmpHeapNoproc(VariableName,Size) \
       Eterm VariableName[Size]
  #define UseTmpHeap(Size,Proc) /* Nothing */
  #define UnUseTmpHeap(Size,Proc) /* Nothing */
  #define UseTmpHeapNoproc(Size) /* Nothing */
  #define UnUseTmpHeapNoproc(Size) /* Nothing */
  
  ERTS_GLB_INLINE void dtrace_pid_str(Eterm pid, char *process_buf);
  ERTS_GLB_INLINE void dtrace_proc_str(Process *process, char *process_buf);
  ERTS_GLB_INLINE void dtrace_port_str(Port *port, char *port_buf);
  ERTS_GLB_INLINE void dtrace_fun_decode(Process *process, ErtsCodeMFA *mfa,
  				       char *process_buf, char *mfa_buf);
  
  #if ERTS_GLB_INLINE_INCL_FUNC_DEF
  
  #include "dtrace-wrapper.h"
  
  ERTS_GLB_INLINE void
  dtrace_pid_str(Eterm pid, char *process_buf)
  {
      if (is_pid(pid))
          erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "<%lu.%lu.%lu>",
                        pid_channel_no(pid),
                        pid_number(pid),
                        pid_serial(pid));
      else if (is_port(pid))
          erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
                        port_channel_no(pid),
                        port_number(pid));
  }
  
  ERTS_GLB_INLINE void
  dtrace_proc_str(Process *process, char *process_buf)
  {
      dtrace_pid_str(process->common.id, process_buf);
  }
  
  ERTS_GLB_INLINE void
  dtrace_port_str(Port *port, char *port_buf)
  {
      dtrace_pid_str(port->common.id, port_buf);
  }
  
  ERTS_GLB_INLINE void
  dtrace_fun_decode(Process *process, ErtsCodeMFA *mfa,
                    char *process_buf, char *mfa_buf)
  {
      if (process_buf) {
          dtrace_proc_str(process, process_buf);
      }
  
      erts_snprintf(mfa_buf, DTRACE_TERM_BUF_SIZE, "%T:%T/%d",
                    mfa->module, mfa->function, mfa->arity);
  }
  
  #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
  
  #endif /* !__GLOBAL_H__ */
  