/*
** Copyright (C) 1995-2000, 2004-2005 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_calls.h - definitions for calls and returns */

#ifndef MERCURY_CALLS_H
#define MERCURY_CALLS_H

#include "mercury_regs.h"	/* for MR_succip */
#include "mercury_goto.h"	/* calls are implemented using gotos */
#include "mercury_debug.h"	/* we need to debug them */
#include "mercury_prof.h"	/* we need to profile them */

/*
** The noprof (no profiling) versions of call, call_localret, localcall,
** tailcall, and localtailcall.
*/

/*
** On some systems [basically those using PIC (Position Independent Code)],
** if we're using gcc non-local gotos to jump between functions then
** we need to do ASM_FIXUP_REGS after each return from a procedure call.
** However, if we're using asm labels, then this is done in the
** MR_define_label(), MR_define_static(), and MR_define_Entry() macros,
** so there's no need to do it here.
** Also if we're using native gc, then the fixup_gp label below
** would stuff up the succip values, so we can't do it.
**
** For the non-asm `jump' and `fast' grades, we do in theory need
** to do something like this.  However, doing ASM_FIXUP_REGS only
** on returns is *not* sufficient to ensure correctness in general.
** So the non-asm `jump' and `fast' grades are in theory broken,
** with or without this code.  In practice they happen to work
** (for our current set of test cases!) either way.
** So it is simpler (and more efficient) to just comment it out.
*/
#if 0
  #define	MR_noprof_call(proc, succ_cont)				\
		({							\
			__label__ fixup_gp;				\
			MR_debugcall((proc), (succ_cont));		\
			MR_succip_word = (MR_Word) (&&fixup_gp);	\
			MR_GOTO(proc);					\
		fixup_gp:						\
			ASM_FIXUP_REGS					\
			MR_GOTO(succ_cont); 				\
		})
		/* same as above, but with MR_GOTO_LABEL rather than MR_GOTO */
  #define	MR_noprof_call_localret(proc, succ_cont)		\
		({							\
			__label__ fixup_gp;				\
			MR_debugcall((proc), (succ_cont));		\
			MR_succip_word = (MR_Word) (&&fixup_gp);	\
			MR_GOTO(proc);					\
		fixup_gp:						\
			ASM_FIXUP_REGS					\
			MR_GOTO_LABEL(succ_cont); 			\
		})
#else
  #define	MR_noprof_call(proc, succ_cont)				\
		do {							\
			MR_debugcall((proc), (succ_cont));		\
			MR_succip_word = (MR_Word) (succ_cont);		\
			MR_GOTO(proc);					\
		} while (0)
  #define 	MR_noprof_call_localret(proc, succ_cont) 		\
		MR_noprof_call((proc), MR_LABEL(succ_cont))
#endif

#define	MR_noprof_localcall(label, succ_cont)				\
		do {							\
			MR_debugcall(MR_LABEL(label), (succ_cont));	\
			MR_succip_word = (MR_Word) (succ_cont);		\
			MR_GOTO_LABEL(label);				\
		} while (0)

#define	MR_noprof_tailcall(proc)					\
		do {							\
			MR_debugtailcall(proc);				\
			MR_GOTO(proc);					\
		} while (0)

#define	MR_noprof_localtailcall(label)					\
		do {							\
			MR_debugtailcall(MR_LABEL(label));		\
			MR_GOTO_LABEL(label);				\
		} while (0)

/*
** The shorthand versions of the non-profiling versions of
** call_localret, localcall, tailcall, and localtailcall.
** To a first approximation, just plain call doesn't occur at all,
** so it isn't worth a shorthand form.
*/

#define	MR_np_call_localret(proc, succ_cont)			\
	MR_noprof_call_localret(MR_add_prefix(proc), MR_add_prefix(succ_cont))

#define	MR_np_call_localret_ent(proc, succ_cont)		\
	MR_noprof_call_localret(MR_ENTRY_AP(proc), MR_add_prefix(succ_cont))

#define	MR_np_localcall(label, succ_cont)			\
	MR_noprof_localcall(MR_add_prefix(label), MR_add_prefix(succ_cont))

#define	MR_np_localcall_ent(label, succ_cont)			\
	MR_noprof_localcall(MR_add_prefix(label), MR_ENTRY_AP(succ_cont))

#define	MR_np_localcall_lab(label, succ_cont)			\
	MR_noprof_localcall(MR_add_prefix(label), MR_LABEL_AP(succ_cont))

#define	MR_np_tailcall(proc)					\
	MR_noprof_tailcall(MR_add_prefix(proc))

#define	MR_np_tailcall_ent(proc)				\
	MR_noprof_tailcall(MR_ENTRY_AP(proc))

#define	MR_np_localtailcall(label)				\
	MR_noprof_localtailcall(MR_add_prefix(label))

/*
** The plain (possibly profiling, depending on #defines) versions of
** call, call_localret, localcall, tailcall, and localtailcall.
** These take an extra argument, the current label.
*/

#define	MR_call(proc, succ_cont, current_label)			\
		do {						\
			MR_PROFILE((proc), (current_label));	\
			MR_set_prof_current_proc(proc);		\
			MR_noprof_call((proc), (succ_cont));	\
		} while (0)

#define	MR_call_localret(proc, succ_cont, current_label)	\
		do {						\
			MR_PROFILE((proc), (current_label));	\
			MR_set_prof_current_proc(proc);		\
			MR_noprof_call_localret(proc, succ_cont);\
		} while (0)

#define	MR_localcall(label, succ_cont, current_label)		\
		do {						\
			MR_PROFILE(MR_LABEL(label), (current_label)); \
			MR_set_prof_current_proc(MR_LABEL(label)); \
			MR_noprof_localcall(label, succ_cont);	\
		} while (0)

#define	MR_tailcall(proc, current_label)			\
		do {						\
			MR_PROFILE((proc), (current_label));	\
			MR_set_prof_current_proc(proc);		\
			MR_noprof_tailcall(proc); 		\
		} while (0)

#define	MR_localtailcall(label, current_label)			\
		do {						\
			MR_PROFILE(MR_LABEL(label), (current_label)); \
			MR_set_prof_current_proc(MR_LABEL(label)); \
			MR_noprof_localtailcall(label); 	\
		} while (0)

/*
** The macros for returning from calls from procedures.
*/

#define	MR_proceed()						\
		do {						\
			MR_debugproceed();			\
			MR_GOTO(MR_succip);			\
		} while (0)

#endif /* not MERCURY_CALLS_H */
