aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/hipe/hipe_mode_switch.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/hipe/hipe_mode_switch.c')
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c641
1 files changed, 641 insertions, 0 deletions
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
new file mode 100644
index 0000000000..e5de244d25
--- /dev/null
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -0,0 +1,641 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/* $Id$
+ * hipe_mode_switch.c
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "erl_process.h"
+#include "beam_load.h" /* which includes beam_opcodes.h */
+#include "beam_catches.h"
+#include "hipe_mode_switch.h"
+#include "bif.h"
+#include "error.h"
+#include "hipe_stack.h"
+#include "hipe_bif0.h" /* hipe_mfa_info_table_init() */
+
+/*
+ * Internal debug support.
+ * #define HIPE_DEBUG to the desired debug level:
+ * 0 no checks
+ * 1 check PCB consistency at mode-switches
+ * 2 log commands and results at mode-switches
+ * 3 log commands, results, and PCB contents at mode-switches
+ *
+ * TODO: check PCB consistency at native BIF calls
+ */
+int hipe_modeswitch_debug = 0;
+
+#define HIPE_DEBUG 0
+
+#if HIPE_DEBUG > 1 /* include DPRINTF() logging */
+
+#define DPRINTF(fmt, args...) \
+do { \
+ if (hipe_modeswitch_debug > 0) { \
+ printf("%s, line %u: " fmt "\r\n", __FUNCTION__, __LINE__ , ##args); \
+ fflush(stdout); \
+ } \
+} while (0)
+
+static const char *code_str(unsigned code)
+{
+ static const char *cmd_str[] = {
+ "call from beam",
+ "return from beam",
+ "throw from beam",
+ "resume from beam",
+ "return to beam",
+ "call to beam",
+ "throw to beam",
+ "suspend to beam",
+ "wait from native",
+ "wait_timeout from native",
+ "trap from native",
+ "call closure from beam",
+ "call closure to beam",
+ };
+ unsigned cmd = code & 0xFF;
+
+ if (cmd < (sizeof(cmd_str)/sizeof(cmd_str[0])))
+ return cmd_str[cmd];
+ else
+ return "???";
+}
+
+#else /* HIPE_DEBUG > 1 */
+
+#define DPRINTF(fmt, args...) do{}while(0)
+
+#endif /* HIPE_DEBUG > 1 */
+
+#if HIPE_DEBUG > 0 /* include HIPE_ASSERT and PCB checking */
+
+static void __noreturn
+hipe_abort(const char *expr, const char *file, unsigned line)
+{
+ erl_exit(1, "ASSERTION FAILED, file %s, line %u: %s\r\n", file, line, expr);
+}
+
+#define HIPE_ASSERT3(expr, file, line) \
+do { \
+ if (!(expr)) \
+ hipe_abort(#expr, file, line); \
+} while (0)
+#define HIPE_ASSERT(expr) HIPE_ASSERT3(expr, __FILE__, __LINE__)
+
+void hipe_check_pcb(Process *p, const char *file, unsigned line)
+{
+#if HIPE_DEBUG > 2
+ if (hipe_modeswitch_debug > 0) {
+ printf("%s, line %u: p %p = {htop %p, stop %p, nstack %p, nsp %p, nstend %p}\r\n", file, line, p, p->htop, p->stop, p->hipe.nstack, p->hipe.nsp, p->hipe.nstend);
+ }
+#endif
+ HIPE_ASSERT3(p != NULL, file, line);
+ HIPE_ASSERT3(p->htop <= p->stop, file, line);
+ HIPE_ASSERT3(p->hipe.nstack <= p->hipe.nstend, file, line);
+ HIPE_ASSERT3(p->hipe.nsp >= p->hipe.nstack, file, line);
+ HIPE_ASSERT3(p->hipe.nsp <= p->hipe.nstend, file, line);
+}
+#define HIPE_CHECK_PCB(P) hipe_check_pcb((P), __FILE__, __LINE__)
+
+#else /* HIPE_DEBUG > 0 */
+
+#define HIPE_ASSERT(expr) do{}while(0)
+#define HIPE_CHECK_PCB(P) do{}while(0)
+
+#endif /* HIPE_DEBUG > 0 */
+
+/* ensure that at least nwords words are available on the native stack */
+static void hipe_check_nstack(Process *p, unsigned nwords);
+
+#if defined(__sparc__)
+#include "hipe_sparc_glue.h"
+#elif defined(__i386__)
+#include "hipe_x86_glue.h"
+#elif defined(__x86_64__)
+#include "hipe_amd64_glue.h"
+#elif defined(__powerpc__) || defined(__ppc__) || defined(__powerpc64__)
+#include "hipe_ppc_glue.h"
+#elif defined(__arm__)
+#include "hipe_arm_glue.h"
+#endif
+
+#define BeamOpCode(Op) ((Uint)BeamOp(Op))
+
+Uint hipe_beam_pc_return[1]; /* needed in hipe_debug.c */
+Uint hipe_beam_pc_throw[1]; /* needed in hipe_debug.c */
+Uint hipe_beam_pc_resume[1]; /* needed by hipe_set_timeout() */
+static Eterm hipe_beam_catch_throw;
+
+void hipe_mode_switch_init(void)
+{
+ hipe_arch_glue_init();
+
+ hipe_beam_pc_return[0] = BeamOpCode(op_hipe_trap_return);
+ hipe_beam_pc_throw[0] = BeamOpCode(op_hipe_trap_throw);
+ hipe_beam_pc_resume[0] = BeamOpCode(op_hipe_trap_resume);
+
+ hipe_beam_catch_throw =
+ make_catch(beam_catches_cons(hipe_beam_pc_throw, BEAM_CATCHES_NIL));
+
+ hipe_mfa_info_table_init();
+}
+
+void hipe_set_call_trap(Uint *bfun, void *nfun, int is_closure)
+{
+ HIPE_ASSERT(bfun[-5] == BeamOpCode(op_i_func_info_IaaI));
+ bfun[0] =
+ is_closure
+ ? BeamOpCode(op_hipe_trap_call_closure)
+ : BeamOpCode(op_hipe_trap_call);
+ bfun[-4] = (Uint)nfun;
+}
+
+static __inline__ void
+hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
+{
+ /* ensure that at least 2 words are available on the BEAM stack */
+ if ((p->stop - 2) < p->htop) {
+ DPRINTF("calling gc to increase BEAM stack size");
+ p->fcalls -= erts_garbage_collect(p, 2, reg, arity);
+ }
+ p->stop -= 2;
+ p->stop[1] = hipe_beam_catch_throw;
+ p->stop[0] = make_cp(p->cp);
+ ++p->catches;
+ p->cp = hipe_beam_pc_return;
+}
+
+static __inline__ void hipe_pop_beam_trap_frame(Process *p)
+{
+ p->cp = cp_val(p->stop[0]);
+ --p->catches;
+ p->stop += 2;
+}
+
+Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
+{
+ unsigned result;
+#if NR_ARG_REGS > 5
+ /* When NR_ARG_REGS > 5, we need to protect the process' input
+ reduction count (which BEAM stores in def_arg_reg[5]) from
+ being clobbered by the arch glue code. */
+ Eterm reds_in = p->def_arg_reg[5];
+#endif
+#if NR_ARG_REGS > 4
+ Eterm o_reds = p->def_arg_reg[4];
+#endif
+
+ p->i = NULL;
+
+ DPRINTF("cmd == %#x (%s)", cmd, code_str(cmd));
+ HIPE_CHECK_PCB(p);
+ p->arity = 0;
+ switch (cmd & 0xFF) {
+ case HIPE_MODE_SWITCH_CMD_CALL: {
+ /* BEAM calls a native code function */
+ unsigned arity = cmd >> 8;
+
+ /* p->hipe.ncallee set in beam_emu */
+ if (p->cp == hipe_beam_pc_return) {
+ /* Native called BEAM, which now tailcalls native. */
+ hipe_pop_beam_trap_frame(p);
+ result = hipe_tailcall_to_native(p, arity, reg);
+ break;
+ }
+ DPRINTF("calling %#lx/%u", (long)p->hipe.ncallee, arity);
+ result = hipe_call_to_native(p, arity, reg);
+ break;
+ }
+ case HIPE_MODE_SWITCH_CMD_CALL_CLOSURE: {
+ /* BEAM calls a native code closure */
+ unsigned arity = cmd >> 8; /* #formals + #fvs (closure not counted) */
+ Eterm fun;
+ ErlFunThing *funp;
+
+ /* drop the fvs, move the closure, correct arity */
+ fun = reg[arity];
+ HIPE_ASSERT(is_fun(fun));
+ funp = (ErlFunThing*)fun_val(fun);
+ HIPE_ASSERT(funp->num_free <= arity);
+ arity -= funp->num_free; /* arity == #formals */
+ reg[arity] = fun;
+ ++arity; /* correct for having added the closure */
+ /* HIPE_ASSERT(p->hipe.ncallee == (void(*)(void))funp->native_address); */
+
+ /* just like a normal call from now on */
+
+ /* p->hipe.ncallee set in beam_emu */
+ if (p->cp == hipe_beam_pc_return) {
+ /* Native called BEAM, which now tailcalls native. */
+ hipe_pop_beam_trap_frame(p);
+ result = hipe_tailcall_to_native(p, arity, reg);
+ break;
+ }
+ DPRINTF("calling %#lx/%u", (long)p->hipe.ncallee, arity);
+ result = hipe_call_to_native(p, arity, reg);
+ break;
+ }
+ case HIPE_MODE_SWITCH_CMD_THROW: {
+ /* BEAM just executed hipe_beam_pc_throw[] */
+ /* Native called BEAM, which now throws an exception back to native. */
+ DPRINTF("beam throws freason %#lx fvalue %#lx", p->freason, p->fvalue);
+ hipe_pop_beam_trap_frame(p);
+ do_throw_to_native:
+ p->def_arg_reg[0] = exception_tag[GET_EXC_CLASS(p->freason)];
+ hipe_find_handler(p);
+ result = hipe_throw_to_native(p);
+ break;
+ }
+ case HIPE_MODE_SWITCH_CMD_RETURN: {
+ /* BEAM just executed hipe_beam_pc_return[] */
+ /* Native called BEAM, which now returns back to native. */
+ /* pop trap frame off estack */
+ hipe_pop_beam_trap_frame(p);
+ p->def_arg_reg[0] = reg[0];
+ result = hipe_return_to_native(p);
+ break;
+ }
+ do_resume:
+ case HIPE_MODE_SWITCH_CMD_RESUME: {
+ /* BEAM just executed hipe_beam_pc_resume[] */
+ /* BEAM called native, which suspended. */
+ if (p->flags & F_TIMO) {
+ /* XXX: The process will immediately execute 'clear_timeout',
+ repeating these two statements. Remove them? */
+ p->flags &= ~F_TIMO;
+ JOIN_MESSAGE(p);
+ p->def_arg_reg[0] = 0; /* make_small(0)? */
+ } else
+ p->def_arg_reg[0] = 1; /* make_small(1)? */
+ result = hipe_return_to_native(p);
+ break;
+ }
+ default:
+ erl_exit(1, "hipe_mode_switch: cmd %#x\r\n", cmd);
+ }
+ do_return_from_native:
+ DPRINTF("result == %#x (%s)", result, code_str(result));
+ HIPE_CHECK_PCB(p);
+ switch (result) {
+ case HIPE_MODE_SWITCH_RES_RETURN: {
+ hipe_return_from_native(p);
+ reg[0] = p->def_arg_reg[0];
+ DPRINTF("returning with r(0) == %#lx", reg[0]);
+ break;
+ }
+ case HIPE_MODE_SWITCH_RES_THROW: {
+ DPRINTF("native throws freason %#lx fvalue %#lx", p->freason, p->fvalue);
+ hipe_throw_from_native(p);
+ break;
+ }
+ case HIPE_MODE_SWITCH_RES_TRAP: {
+ /*
+ * Native code called a BIF, which "failed" with a TRAP to BEAM.
+ * Prior to returning, the BIF stored (see BIF_TRAP<N>):
+
+ * the callee's address in p->def_arg_reg[3]
+ * the callee's parameters in p->def_arg_reg[0..2]
+ * the callee's arity in p->arity (for BEAM gc purposes)
+ *
+ * We need to remove the BIF's parameters from the native
+ * stack: to this end hipe_${ARCH}_glue.S stores the BIF's
+ * arity in p->hipe.narity.
+ */
+ unsigned int i, is_recursive, callee_arity;
+
+ /* Save p->arity, then update it with the original BIF's arity.
+ Get rid of any stacked parameters in that call. */
+ /* XXX: hipe_call_from_native_is_recursive() copies data to
+ reg[], which is useless in the TRAP case. Maybe write a
+ specialised hipe_trap_from_native_is_recursive() later. */
+ callee_arity = p->arity;
+ p->arity = p->hipe.narity; /* caller's arity */
+ is_recursive = hipe_call_from_native_is_recursive(p, reg);
+
+ p->i = (Eterm *)(p->def_arg_reg[3]);
+ p->arity = callee_arity;
+
+ for (i = 0; i < p->arity; ++i)
+ reg[i] = p->def_arg_reg[i];
+
+ if (is_recursive)
+ hipe_push_beam_trap_frame(p, reg, p->arity);
+
+ result = HIPE_MODE_SWITCH_RES_CALL;
+ break;
+ }
+ case HIPE_MODE_SWITCH_RES_CALL: {
+ /* Native code calls or tailcalls BEAM.
+ *
+ * p->i is the callee's BEAM code
+ * p->arity is the callee's arity
+ * p->def_arg_reg[] contains the register parameters
+ * p->hipe.nsp[] contains the stacked parameters
+ */
+ if (hipe_call_from_native_is_recursive(p, reg)) {
+ /* BEAM called native, which now calls BEAM */
+ hipe_push_beam_trap_frame(p, reg, p->arity);
+ }
+ break;
+ }
+ case HIPE_MODE_SWITCH_RES_CALL_CLOSURE: {
+ /* Native code calls or tailcalls a closure in BEAM
+ *
+ * In native code a call to a closure of arity n looks like
+ * F(A1, ..., AN, Closure),
+ * The BEAM code for a closure expects to get:
+ * F(A1, ..., AN, FV1, ..., FVM, Closure)
+ * (Where Ai is argument i and FVj is free variable j)
+ *
+ * p->hipe.closure contains the closure
+ * p->def_arg_reg[] contains the register parameters
+ * p->hipe.nsp[] contains the stacked parameters
+ */
+ ErlFunThing *closure;
+ unsigned num_free, arity, i, is_recursive;
+
+ HIPE_ASSERT(is_fun(p->hipe.closure));
+ closure = (ErlFunThing*)fun_val(p->hipe.closure);
+ num_free = closure->num_free;
+ arity = closure->fe->arity;
+
+ /* Store the arity in p->arity for the stack popping. */
+ /* Note: we already have the closure so only need to move arity
+ values to reg[]. However, there are arity+1 parameters in the
+ native code state that need to be removed. */
+ p->arity = arity+1; /* +1 for the closure */
+
+ /* Get parameters, don't do GC just yet. */
+ is_recursive = hipe_call_from_native_is_recursive(p, reg);
+
+ if ((Sint)closure->fe->address[-1] < 0) {
+ /* Unloaded. Let beam_emu.c:call_fun() deal with it. */
+ result = HIPE_MODE_SWITCH_RES_CALL_CLOSURE;
+ } else {
+ /* The BEAM code is present. Prepare to call it. */
+
+ /* Append the free vars after the actual parameters. */
+ for (i = 0; i < num_free; ++i)
+ reg[arity+i] = closure->env[i];
+
+ /* Update arity to reflect the new parameters. */
+ arity += i;
+
+ /* Make a call to the closure's BEAM code. */
+ p->i = closure->fe->address;
+
+ /* Change result code to the faster plain CALL type. */
+ result = HIPE_MODE_SWITCH_RES_CALL;
+ }
+ /* Append the closure as the last parameter. Don't increment arity. */
+ reg[arity] = p->hipe.closure;
+
+ if (is_recursive) {
+ /* BEAM called native, which now calls BEAM.
+ Need to put a trap-frame on the beam stack.
+ This may cause GC, which is safe now that
+ the arguments, free vars, and most
+ importantly the closure, all are in reg[]. */
+ hipe_push_beam_trap_frame(p, reg, arity+1);
+ }
+ break;
+ }
+ case HIPE_MODE_SWITCH_RES_SUSPEND: {
+ p->i = hipe_beam_pc_resume;
+ p->arity = 0;
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (p->status != P_SUSPENDED)
+ erts_add_to_runq(p);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ goto do_schedule;
+ }
+ case HIPE_MODE_SWITCH_RES_WAIT:
+ case HIPE_MODE_SWITCH_RES_WAIT_TIMEOUT: {
+ /* same semantics, different debug trace messages */
+#ifdef ERTS_SMP
+ /* XXX: BEAM has different entries for the locked and unlocked
+ cases. HiPE doesn't, so we must check dynamically. */
+ if (p->hipe_smp.have_receive_locks)
+ p->hipe_smp.have_receive_locks = 0;
+ else
+ erts_smp_proc_lock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+#endif
+ p->i = hipe_beam_pc_resume;
+ p->arity = 0;
+ p->status = P_WAITING;
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE);
+ do_schedule:
+ {
+#if !(NR_ARG_REGS > 5)
+ int reds_in = p->def_arg_reg[5];
+#endif
+ p = schedule(p, reds_in - p->fcalls);
+#ifdef ERTS_SMP
+ p->hipe_smp.have_receive_locks = 0;
+ reg = p->scheduler_data->save_reg;
+#endif
+ }
+ {
+ Eterm *argp;
+ int i;
+
+ argp = p->arg_reg;
+ for (i = p->arity; --i >= 0;)
+ reg[i] = argp[i];
+ }
+ {
+#if !(NR_ARG_REGS > 5)
+ Eterm reds_in;
+#endif
+#if !(NR_ARG_REGS > 4)
+ Eterm o_reds;
+#endif
+
+ reds_in = p->fcalls;
+ o_reds = 0;
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(p)) {
+ o_reds = reds_in;
+ reds_in = 0;
+ p->fcalls = 0;
+ }
+ p->def_arg_reg[4] = o_reds;
+ p->def_arg_reg[5] = reds_in;
+ if (p->i == hipe_beam_pc_resume) {
+ p->i = NULL;
+ p->arity = 0;
+ goto do_resume;
+ }
+ }
+ HIPE_CHECK_PCB(p);
+ result = HIPE_MODE_SWITCH_RES_CALL;
+ p->def_arg_reg[3] = result;
+ return p;
+ }
+ case HIPE_MODE_SWITCH_RES_APPLY: {
+ Eterm mfa[3], args;
+ unsigned int arity;
+ void *address;
+
+ hipe_pop_params(p, 3, &mfa[0]);
+
+ /* Unroll the arglist onto reg[]. */
+ args = mfa[2];
+ arity = 0;
+ while (is_list(args)) {
+ if (arity < 255) {
+ reg[arity++] = CAR(list_val(args));
+ args = CDR(list_val(args));
+ } else
+ goto do_apply_fail;
+ }
+ if (is_not_nil(args))
+ goto do_apply_fail;
+
+ /* find a native code entry point for {M,F,A} for a remote call */
+ address = hipe_get_remote_na(mfa[0], mfa[1], arity);
+ if (!address)
+ goto do_apply_fail;
+ p->hipe.ncallee = (void(*)(void)) address;
+ result = hipe_tailcall_to_native(p, arity, reg);
+ goto do_return_from_native;
+ do_apply_fail:
+ p->freason = BADARG;
+ goto do_throw_to_native;
+ }
+ default:
+ erl_exit(1, "hipe_mode_switch: result %#x\r\n", result);
+ }
+ HIPE_CHECK_PCB(p);
+ p->def_arg_reg[3] = result;
+#if NR_ARG_REGS > 4
+ p->def_arg_reg[4] = o_reds;
+#endif
+#if NR_ARG_REGS > 5
+ p->def_arg_reg[5] = reds_in;
+#endif
+ return p;
+}
+
+#define HIPE_INITIAL_NSTACK_SIZE 128
+
+/* PRE: size is zero or a power of two */
+static unsigned hipe_next_nstack_size(unsigned size)
+{
+ return size ? size * 2 : HIPE_INITIAL_NSTACK_SIZE;
+}
+
+#if 0 && defined(HIPE_NSTACK_GROWS_UP)
+#define hipe_nstack_avail(p) ((p)->hipe.nstend - (p)->hipe.nsp)
+void hipe_inc_nstack(Process *p)
+{
+ Eterm *old_nstack = p->hipe.nstack;
+ unsigned old_size = p->hipe.nstend - old_nstack;
+ unsigned new_size = hipe_next_nstack_size(old_size);
+ Eterm *new_nstack = erts_realloc(ERTS_ALC_T_HIPE,
+ (char *) old_nstack,
+ new_size*sizeof(Eterm));
+ p->hipe.nstend = new_nstack + new_size;
+ if (new_nstack != old_nstack) {
+ p->hipe.nsp = new_nstack + (p->hipe.nsp - old_nstack);
+ p->hipe.nstack = new_nstack;
+ if (p->hipe.nstgraylim)
+ p->hipe.nstgraylim =
+ new_nstack + (p->hipe.nstgraylim - old_nstack);
+ if (p->hipe.nstblacklim)
+ p->hipe.nstblacklim =
+ new_nstack + (p->hipe.nstblacklim - old_nstack);
+ }
+}
+#endif
+
+#if defined(HIPE_NSTACK_GROWS_DOWN)
+#define hipe_nstack_avail(p) ((unsigned)((p)->hipe.nsp - (p)->hipe.nstack))
+void hipe_inc_nstack(Process *p)
+{
+ unsigned old_size = p->hipe.nstend - p->hipe.nstack;
+ unsigned new_size = hipe_next_nstack_size(old_size);
+ Eterm *new_nstack = erts_alloc(ERTS_ALC_T_HIPE, new_size*sizeof(Eterm));
+ unsigned used_size = p->hipe.nstend - p->hipe.nsp;
+
+ sys_memcpy(new_nstack+new_size-used_size, p->hipe.nsp, used_size*sizeof(Eterm));
+ if (p->hipe.nstgraylim)
+ p->hipe.nstgraylim = new_nstack + new_size - (p->hipe.nstend - p->hipe.nstgraylim);
+ if (p->hipe.nstblacklim)
+ p->hipe.nstblacklim = new_nstack + new_size - (p->hipe.nstend - p->hipe.nstblacklim);
+ if (p->hipe.nstack)
+ erts_free(ERTS_ALC_T_HIPE, p->hipe.nstack);
+ p->hipe.nstack = new_nstack;
+ p->hipe.nstend = new_nstack + new_size;
+ p->hipe.nsp = new_nstack + new_size - used_size;
+}
+#endif
+
+static void hipe_check_nstack(Process *p, unsigned nwords)
+{
+ while (hipe_nstack_avail(p) < nwords)
+ hipe_inc_nstack(p);
+}
+
+void hipe_set_closure_stub(ErlFunEntry *fe, unsigned num_free)
+{
+ unsigned arity;
+
+ arity = fe->arity;
+ fe->native_address = (Eterm*) hipe_closure_stub_address(arity);
+}
+
+Eterm hipe_build_stacktrace(Process *p, struct StackTrace *s)
+{
+ int depth, i;
+ Uint heap_size;
+ Eterm *hp, *hp_end, mfa, m, f, head, *next_p, next;
+ const void *ra;
+ unsigned int a;
+
+ depth = s->depth;
+ if (depth < 1)
+ return NIL;
+
+ heap_size = 6 * depth; /* each [{M,F,A}|_] is 2+4 == 6 words */
+ hp = HAlloc(p, heap_size);
+ hp_end = hp + heap_size;
+
+ head = NIL;
+ next_p = &head;
+
+ for (i = 0; i < depth; ++i) {
+ ra = (const void*)s->trace[i];
+ if (!hipe_find_mfa_from_ra(ra, &m, &f, &a))
+ continue;
+ mfa = TUPLE3(hp, m, f, make_small(a));
+ hp += 4;
+ next = CONS(hp, mfa, NIL);
+ *next_p = next;
+ next_p = &CDR(list_val(next));
+ hp += 2;
+ }
+ HRelease(p, hp_end, hp);
+ return head;
+}