/erts/emulator/beam/beam_emu.c
C | 5921 lines | 4453 code | 636 blank | 832 comment | 852 complexity | a1d0c5b6e5b28b84d7bfa68f8386ca83 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, LGPL-2.1
Large files files are truncated, but you can click here to view the full file
- /* ``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 via the world wide web 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.
- *
- * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
- * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
- * AB. All Rights Reserved.''
- *
- * $Id$
- */
- #ifdef HAVE_CONFIG_H
- # include "config.h"
- #endif
- #include <stddef.h> /* offsetof() */
- #include "sys.h"
- #include "erl_vm.h"
- #include "global.h"
- #include "erl_process.h"
- #include "erl_nmgc.h"
- #include "error.h"
- #include "bif.h"
- #include "big.h"
- #include "beam_load.h"
- #include "erl_binary.h"
- #include "erl_bits.h"
- #include "dist.h"
- #include "beam_bp.h"
- #include "beam_catches.h"
- #ifdef HIPE
- #include "hipe_mode_switch.h"
- #include "hipe_bif1.h"
- #endif
- /* #define HARDDEBUG 1 */
- #if defined(NO_JUMP_TABLE)
- # define OpCase(OpCode) case op_##OpCode: lb_##OpCode
- # define CountCase(OpCode) case op_count_##OpCode
- # define OpCode(OpCode) ((Uint*)op_##OpCode)
- # define Goto(Rel) {Go = (int)(Rel); goto emulator_loop;}
- # define LabelAddr(Addr) &&##Addr
- #else
- # define OpCase(OpCode) lb_##OpCode
- # define CountCase(OpCode) lb_count_##OpCode
- # define Goto(Rel) goto *(Rel)
- # define LabelAddr(Label) &&Label
- # define OpCode(OpCode) (&&lb_##OpCode)
- #endif
- #ifdef ERTS_ENABLE_LOCK_CHECK
- # ifdef ERTS_SMP
- # define PROCESS_MAIN_CHK_LOCKS(P) \
- do { \
- if ((P)) \
- erts_proc_lc_chk_only_proc_main((P)); \
- else \
- erts_lc_check_exact(NULL, 0); \
- ERTS_SMP_LC_ASSERT(!ERTS_LC_IS_BLOCKING); \
- } while (0)
- # define ERTS_SMP_REQ_PROC_MAIN_LOCK(P) \
- if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN)
- # define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P) \
- if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN)
- # else
- # define ERTS_SMP_REQ_PROC_MAIN_LOCK(P)
- # define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P)
- # define PROCESS_MAIN_CHK_LOCKS(P) erts_lc_check_exact(NULL, 0)
- # endif
- #else
- # define PROCESS_MAIN_CHK_LOCKS(P)
- # define ERTS_SMP_REQ_PROC_MAIN_LOCK(P)
- # define ERTS_SMP_UNREQ_PROC_MAIN_LOCK(P)
- #endif
- /*
- * Shallow copy to heap if possible; otherwise,
- * move to heap via garbage collection.
- */
- #define MV_MSG_MBUF_INTO_PROC(M) \
- do { \
- if ((M)->bp) { \
- Uint need = (M)->bp->size; \
- if (E - HTOP >= need) { \
- Uint *htop = HTOP; \
- erts_move_msg_mbuf_to_heap(&htop, &MSO(c_p), (M)); \
- ASSERT(htop - HTOP == need); \
- HTOP = htop; \
- } \
- else { \
- SWAPOUT; \
- reg[0] = r(0); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, 0, NULL, 0); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- r(0) = reg[0]; \
- SWAPIN; \
- ASSERT(!(M)->bp); \
- } \
- } \
- ASSERT(!(M)->bp); \
- } while (0)
- /*
- * Define macros for deep checking of terms.
- */
- #if defined(HARDDEBUG)
- # define CHECK_TERM(T) size_object(T)
- # define CHECK_ARGS(PC) \
- do { \
- int i_; \
- int Arity_ = PC[-1]; \
- if (Arity_ > 0) { \
- CHECK_TERM(r(0)); \
- } \
- for (i_ = 1; i_ < Arity_; i_++) { \
- CHECK_TERM(x(i_)); \
- } \
- } while (0)
-
- #else
- # define CHECK_TERM(T) ASSERT(!is_CP(T))
- # define CHECK_ARGS(T)
- #endif
- #ifndef MAX
- #define MAX(x, y) (((x) > (y)) ? (x) : (y))
- #endif
- #define GET_BIF_ADDRESS(p) ((BifFunction) (((Export *) p)->code[4]))
- /*
- * We reuse some of fields in the save area in the process structure.
- * This is safe to do, since this space is only activly used when
- * the process is switched out.
- */
- #define REDS_IN(p) ((p)->def_arg_reg[5])
- /*
- * Add a byte offset to a pointer to Eterm. This is useful when the
- * the loader has precalculated a byte offset.
- */
- #define ADD_BYTE_OFFSET(ptr, offset) \
- ((Eterm *) (((unsigned char *)ptr) + (offset)))
- /* We don't check the range if an ordinary switch is used */
- #ifdef NO_JUMP_TABLE
- #define VALID_INSTR(IP) (0 <= (int)(IP) && ((int)(IP) < (NUMBER_OF_OPCODES*2+10)))
- #else
- #define VALID_INSTR(IP) \
- ((Sint)LabelAddr(emulator_loop) <= (Sint)(IP) && \
- (Sint)(IP) < (Sint)LabelAddr(end_emulator_loop))
- #endif /* NO_JUMP_TABLE */
- #define SET_CP(p, ip) \
- ASSERT(VALID_INSTR(*(ip))); \
- (p)->cp = (ip)
- #define SET_I(ip) \
- ASSERT(VALID_INSTR(* (Eterm *)(ip))); \
- I = (ip)
- #define FetchArgs(S1, S2) tmp_arg1 = (S1); tmp_arg2 = (S2)
- /*
- * Store a result into a register given a destination descriptor.
- */
- #define StoreResult(Result, DestDesc) \
- do { \
- Eterm stb_reg; \
- stb_reg = (DestDesc); \
- CHECK_TERM(Result); \
- switch (beam_reg_tag(stb_reg)) { \
- case R_REG_DEF: \
- r(0) = (Result); break; \
- case X_REG_DEF: \
- xb(x_reg_offset(stb_reg)) = (Result); break; \
- default: \
- yb(y_reg_offset(stb_reg)) = (Result); break; \
- } \
- } while (0)
- #define StoreSimpleDest(Src, Dest) Dest = (Src)
- /*
- * Store a result into a register and execute the next instruction.
- * Dst points to the word with a destination descriptor, which MUST
- * be just before the next instruction.
- */
-
- #define StoreBifResult(Dst, Result) \
- do { \
- Eterm* stb_next; \
- Eterm stb_reg; \
- stb_reg = Arg(Dst); \
- I += (Dst) + 2; \
- stb_next = (Eterm *) *I; \
- CHECK_TERM(Result); \
- switch (beam_reg_tag(stb_reg)) { \
- case R_REG_DEF: \
- r(0) = (Result); Goto(stb_next); \
- case X_REG_DEF: \
- xb(x_reg_offset(stb_reg)) = (Result); Goto(stb_next); \
- default: \
- yb(y_reg_offset(stb_reg)) = (Result); Goto(stb_next); \
- } \
- } while (0)
- #define ClauseFail() goto lb_jump_f
- #define SAVE_CP(X) *(X) = make_cp(c_p->cp)
- #define RESTORE_CP(X) SET_CP(c_p, cp_val(*(X)))
- #define ISCATCHEND(instr) ((Eterm *) *(instr) == OpCode(catch_end_y))
- /*
- * Special Beam instructions.
- */
- Eterm beam_apply[2];
- Eterm beam_exit[1];
- Eterm* em_call_error_handler;
- Eterm* em_apply_bif;
- Eterm* em_call_traced_function;
- /* NOTE These should be the only variables containing trace instructions.
- ** Sometimes tests are form the instruction value, and sometimes
- ** for the refering variable (one of these), and rouge references
- ** will most likely cause chaos.
- */
- Eterm beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */
- Eterm beam_return_trace[1]; /* OpCode(i_return_trace) */
- Eterm beam_exception_trace[1]; /* UGLY also OpCode(i_return_trace) */
- /*
- * All Beam instructions in numerical order.
- */
- #ifndef NO_JUMP_TABLE
- void** beam_ops;
- #endif
- #ifndef ERTS_SMP /* Not supported with smp emulator */
- extern int count_instructions;
- #endif
- #if defined(HYBRID)
- #define SWAPIN \
- g_htop = global_htop; \
- g_hend = global_hend; \
- HTOP = HEAP_TOP(c_p); \
- E = c_p->stop
- #define SWAPOUT \
- global_htop = g_htop; \
- global_hend = g_hend; \
- HEAP_TOP(c_p) = HTOP; \
- c_p->stop = E
- #else
- #define SWAPIN \
- HTOP = HEAP_TOP(c_p); \
- E = c_p->stop
- #define SWAPOUT \
- HEAP_TOP(c_p) = HTOP; \
- c_p->stop = E
- #endif
- #define PRE_BIF_SWAPOUT(P) \
- HEAP_TOP((P)) = HTOP; \
- (P)->stop = E; \
- PROCESS_MAIN_CHK_LOCKS((P)); \
- ERTS_SMP_UNREQ_PROC_MAIN_LOCK((P))
- #if defined(HYBRID)
- # define POST_BIF_GC_SWAPIN_0(_p, _res) \
- if ((_p)->mbuf) { \
- _res = erts_gc_after_bif_call((_p), (_res), NULL, 0); \
- } \
- SWAPIN
- # define POST_BIF_GC_SWAPIN(_p, _res, _regs, _arity) \
- if ((_p)->mbuf) { \
- _regs[0] = r(0); \
- _res = erts_gc_after_bif_call((_p), (_res), _regs, (_arity)); \
- r(0) = _regs[0]; \
- } \
- SWAPIN
- #else
- # define POST_BIF_GC_SWAPIN_0(_p, _res) \
- ERTS_SMP_REQ_PROC_MAIN_LOCK((_p)); \
- PROCESS_MAIN_CHK_LOCKS((_p)); \
- if ((_p)->mbuf) { \
- _res = erts_gc_after_bif_call((_p), (_res), NULL, 0); \
- E = (_p)->stop; \
- } \
- HTOP = HEAP_TOP((_p))
- # define POST_BIF_GC_SWAPIN(_p, _res, _regs, _arity) \
- ERTS_SMP_REQ_PROC_MAIN_LOCK((_p)); \
- PROCESS_MAIN_CHK_LOCKS((_p)); \
- if ((_p)->mbuf) { \
- _regs[0] = r(0); \
- _res = erts_gc_after_bif_call((_p), (_res), _regs, (_arity)); \
- r(0) = _regs[0]; \
- E = (_p)->stop; \
- } \
- HTOP = HEAP_TOP((_p))
- #endif
- #define SAVE_HTOP HEAP_TOP(c_p) = HTOP
- #define db(N) (N)
- #define tb(N) (N)
- #define xb(N) (*(Eterm *) (((unsigned char *)reg) + (N)))
- #define yb(N) (*(Eterm *) (((unsigned char *)E) + (N)))
- #define fb(N) (*(double *) (((unsigned char *)&(freg[0].fd)) + (N)))
- #define x(N) reg[N]
- #define y(N) E[N]
- #define r(N) x##N
- /*
- * Makes sure that there are StackNeed + HeapNeed + 1 words available
- * on the combined heap/stack segment, then allocates StackNeed + 1
- * words on the stack and saves CP.
- *
- * M is number of live registers to preserve during garbage collection
- */
- #define AH(StackNeed, HeapNeed, M) \
- do { \
- int needed; \
- needed = (StackNeed) + 1; \
- if (E - HTOP < (needed + (HeapNeed))) { \
- SWAPOUT; \
- reg[0] = r(0); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, needed + (HeapNeed), reg, (M)); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- r(0) = reg[0]; \
- SWAPIN; \
- } \
- E -= needed; \
- SAVE_CP(E); \
- } while (0)
- #define Allocate(Ns, Live) AH(Ns, 0, Live)
- #define AllocateZero(Ns, Live) \
- do { Eterm* ptr; \
- int i = (Ns); \
- AH(i, 0, Live); \
- for (ptr = E + i; ptr > E; ptr--) { \
- make_blank(*ptr); \
- } \
- } while (0)
- #define AllocateHeap(Ns, Nh, Live) AH(Ns, Nh, Live)
- #define AllocateHeapZero(Ns, Nh, Live) \
- do { Eterm* ptr; \
- int i = (Ns); \
- AH(i, Nh, Live); \
- for (ptr = E + i; ptr > E; ptr--) { \
- make_blank(*ptr); \
- } \
- } while (0)
- #define AllocateInit(Ns, Live, Y) \
- do { AH(Ns, 0, Live); make_blank(Y); } while (0)
- /*
- * Like the AH macro, but allocates no additional heap space.
- */
- #define A(StackNeed, M) AH(StackNeed, 0, M)
- #define D(N) \
- RESTORE_CP(E); \
- E += (N) + 1;
- /*
- * Check if Nh words of heap are available; if not, do a garbage collection.
- * Live is number of active argument registers to be preserved.
- */
- #define TestHeap(Nh, Live) \
- do { \
- unsigned need = (Nh); \
- if (E - HTOP < need) { \
- SWAPOUT; \
- reg[0] = r(0); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- r(0) = reg[0]; \
- SWAPIN; \
- } \
- } while (0)
- /*
- * Check if Nh words of heap are available; if not, do a garbage collection.
- * Live is number of active argument registers to be preserved.
- * Takes special care to preserve Extra if a garbage collection occurs.
- */
- #define TestHeapPreserve(Nh, Live, Extra) \
- do { \
- unsigned need = (Nh); \
- if (E - HTOP < need) { \
- SWAPOUT; \
- reg[0] = r(0); \
- reg[Live] = Extra; \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)+1); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- if (Live > 0) { \
- r(0) = reg[0]; \
- } \
- Extra = reg[Live]; \
- SWAPIN; \
- } \
- } while (0)
- #ifdef HYBRID
- #ifdef INCREMENTAL
- #define TestGlobalHeap(Nh, Live, hp) \
- do { \
- unsigned need = (Nh); \
- ASSERT(global_heap <= g_htop && g_htop <= global_hend); \
- SWAPOUT; \
- reg[0] = r(0); \
- FCALLS -= need; \
- (hp) = IncAlloc(c_p,need,reg,(Live)); \
- r(0) = reg[0]; \
- SWAPIN; \
- } while (0)
- #else
- #define TestGlobalHeap(Nh, Live, hp) \
- do { \
- unsigned need = (Nh); \
- ASSERT(global_heap <= g_htop && g_htop <= global_hend); \
- if (g_hend - g_htop < need) { \
- SWAPOUT; \
- reg[0] = r(0); \
- FCALLS -= erts_global_garbage_collect(c_p, need, reg, (Live)); \
- r(0) = reg[0]; \
- SWAPIN; \
- } \
- (hp) = global_htop; \
- } while (0)
- #endif
- #endif /* HYBRID */
- #define Init(N) make_blank(yb(N))
- #define Init2(Y1, Y2) do { make_blank(Y1); make_blank(Y2); } while (0)
- #define Init3(Y1, Y2, Y3) \
- do { make_blank(Y1); make_blank(Y2); make_blank(Y3); } while (0)
- #define MakeFun(FunP, NumFree) \
- do { \
- SWAPOUT; \
- reg[0] = r(0); \
- r(0) = new_fun(c_p, reg, (ErlFunEntry *) FunP, NumFree); \
- SWAPIN; \
- } while (0)
- /*
- * Check that we haven't used the reductions and jump to function pointed to by
- * the I register. If we are out of reductions, do a context switch.
- */
- #define DispatchMacro() \
- do { \
- Eterm* dis_next; \
- dis_next = (Eterm *) *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch; \
- } \
- } while (0)
- #define DispatchMacroFun() \
- do { \
- Eterm* dis_next; \
- dis_next = (Eterm *) *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch_fun; \
- } \
- } while (0)
- #define DispatchMacrox() \
- do { \
- if (FCALLS > 0) { \
- Eterm* dis_next; \
- SET_I(((Export *) Arg(0))->address); \
- dis_next = (Eterm *) *I; \
- FCALLS--; \
- CHECK_ARGS(I); \
- Goto(dis_next); \
- } else if (c_p->ct != NULL && FCALLS > neg_o_reds) { \
- goto save_calls1; \
- } else { \
- SET_I(((Export *) Arg(0))->address); \
- CHECK_ARGS(I); \
- goto context_switch; \
- } \
- } while (0)
- #ifdef DEBUG
- /*
- * To simplify breakpoint setting, put the code in one place only and jump to it.
- */
- # define Dispatch() goto do_dispatch
- # define Dispatchx() goto do_dispatchx
- # define Dispatchfun() goto do_dispatchfun
- #else
- /*
- * Inline for speed.
- */
- # define Dispatch() DispatchMacro()
- # define Dispatchx() DispatchMacrox()
- # define Dispatchfun() DispatchMacroFun()
- #endif
- #define Self(R) R = c_p->id
- #define Node(R) R = erts_this_node->sysname
- #define Arg(N) I[(N)+1]
- #define Next(N) \
- I += (N) + 1; \
- ASSERT(VALID_INSTR(*I)); \
- Goto(*I)
- #define PreFetch(N, Dst) do { Dst = (Eterm *) *(I + N + 1); } while (0)
- #define NextPF(N, Dst) \
- I += N + 1; \
- ASSERT(VALID_INSTR(Dst)); \
- Goto(Dst)
- #define GetR(pos, tr) \
- do { \
- tr = Arg(pos); \
- switch (beam_reg_tag(tr)) { \
- case R_REG_DEF: tr = r(0); break; \
- case X_REG_DEF: tr = xb(x_reg_offset(tr)); break; \
- case Y_REG_DEF: ASSERT(y_reg_offset(tr) >= 1); tr = yb(y_reg_offset(tr)); break; \
- } \
- CHECK_TERM(tr); \
- } while (0)
- #define GetArg1(N, Dst) GetR((N), Dst)
- #define GetArg2(N, Dst1, Dst2) \
- do { \
- GetR(N, Dst1); \
- GetR((N)+1, Dst2); \
- } while (0)
- #define PutList(H, T, Dst, Store) \
- do { \
- HTOP[0] = (H); HTOP[1] = (T); \
- Store(make_list(HTOP), Dst); \
- HTOP += 2; \
- } while (0)
- #define Move(Src, Dst, Store) \
- do { \
- Eterm term = (Src); \
- Store(term, Dst); \
- } while (0)
- #define Move2(src1, dst1, src2, dst2) dst1 = (src1); dst2 = (src2)
- #define MoveGenDest(src, dstp) \
- if ((dstp) == NULL) { r(0) = (src); } else { *(dstp) = src; }
- #define MoveReturn(Src, Dest) \
- (Dest) = (Src); \
- I = c_p->cp; \
- ASSERT(VALID_INSTR(*c_p->cp)); \
- CHECK_TERM(r(0)); \
- Goto(*I)
- #define DeallocateReturn(Deallocate) \
- do { \
- int words_to_pop = (Deallocate); \
- SET_I(cp_val(*E)); \
- E = ADD_BYTE_OFFSET(E, words_to_pop); \
- CHECK_TERM(r(0)); \
- Goto(*I); \
- } while (0)
- #define MoveDeallocateReturn(Src, Dest, Deallocate) \
- (Dest) = (Src); \
- DeallocateReturn(Deallocate)
- #define MoveCall(Src, Dest, CallDest, Size) \
- (Dest) = (Src); \
- SET_CP(c_p, I+Size+1); \
- SET_I((Eterm *) CallDest); \
- Dispatch();
- #define MoveCallLast(Src, Dest, CallDest, Deallocate) \
- (Dest) = (Src); \
- RESTORE_CP(E); \
- E = ADD_BYTE_OFFSET(E, (Deallocate)); \
- SET_I((Eterm *) CallDest); \
- Dispatch();
- #define MoveCallOnly(Src, Dest, CallDest) \
- (Dest) = (Src); \
- SET_I((Eterm *) CallDest); \
- Dispatch();
- #define GetList(Src, H, T) do { \
- Eterm* tmp_ptr = list_val(Src); \
- H = CAR(tmp_ptr); \
- T = CDR(tmp_ptr); } while (0)
- #define GetTupleElement(Src, Element, Dest) \
- do { \
- tmp_arg1 = (Eterm) (((unsigned char *) tuple_val(Src)) + (Element)); \
- (Dest) = (*(Eterm *)tmp_arg1); \
- } while (0)
- #define ExtractNextElement(Dest) \
- tmp_arg1 += sizeof(Eterm); \
- (Dest) = (* (Eterm *) (((unsigned char *) tmp_arg1)))
- #define ExtractNextElement2(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \
- ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \
- tmp_arg1 += sizeof(Eterm) + sizeof(Eterm); \
- } while (0)
- #define ExtractNextElement3(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \
- ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \
- ene_dstp[2] = ((Eterm *) tmp_arg1)[3]; \
- tmp_arg1 += 3*sizeof(Eterm); \
- } while (0)
- #define ExtractNextElement4(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) tmp_arg1)[1]; \
- ene_dstp[1] = ((Eterm *) tmp_arg1)[2]; \
- ene_dstp[2] = ((Eterm *) tmp_arg1)[3]; \
- ene_dstp[3] = ((Eterm *) tmp_arg1)[4]; \
- tmp_arg1 += 4*sizeof(Eterm); \
- } while (0)
- #define ExtractElement(Element, Dest) \
- do { \
- tmp_arg1 += (Element); \
- (Dest) = (* (Eterm *) tmp_arg1); \
- } while (0)
- #define PutTuple(Arity, Src, Dest) \
- ASSERT(is_arity_value(Arity)); \
- Dest = make_tuple(HTOP); \
- HTOP[0] = (Arity); \
- HTOP[1] = (Src); \
- HTOP += 2
- #define Put(Word) *HTOP++ = (Word)
- #define EqualImmed(X, Y, Action) if (X != Y) { Action; }
- #define IsFloat(Src, Fail) if (is_not_float(Src)) { Fail; }
- #define IsInteger(Src, Fail) if (is_not_integer(Src)) { Fail; }
- #define IsNumber(X, Fail) if (is_not_integer(X) && is_not_float(X)) { Fail; }
- #define IsConstant(X, Fail) if (is_list(X) || is_nil(X) || is_tuple(X)) { Fail; }
- #define IsAtom(Src, Fail) if (is_not_atom(Src)) { Fail; }
- #define IsIntegerAllocate(Src, Need, Alive, Fail) \
- if (is_not_integer(Src)) { Fail; } \
- A(Need, Alive)
- #define IsNil(Src, Fail) if (is_not_nil(Src)) { Fail; }
- #define IsList(Src, Fail) if (is_not_list(Src) && is_not_nil(Src)) { Fail; }
- #define IsNonemptyList(Src, Fail) if (is_not_list(Src)) { Fail; }
- #define IsNonemptyListAllocate(Src, Need, Alive, Fail) \
- if (is_not_list(Src)) { Fail; } \
- A(Need, Alive)
- #define IsNonemptyListTestHeap(Src, Need, Alive, Fail) \
- if (is_not_list(Src)) { Fail; } \
- TestHeap(Need, Alive)
- #define IsTuple(X, Action) if (is_not_tuple(X)) Action
- #define IsArity(Pointer, Arity, Fail) \
- if (*(Eterm *)(tmp_arg1 = (Eterm)tuple_val(Pointer)) != (Arity)) { Fail; }
- #define IsFunction(X, Action) \
- do { \
- if ( !(is_any_fun(X)) ) { \
- Action; \
- } \
- } while (0)
- #define IsFunction2(F, A, Action) \
- do { \
- if (is_function_2(c_p, F, A) != am_true ) {\
- Action; \
- } \
- } while (0)
- #define IsTupleOfArity(Src, Arity, Fail) \
- do { \
- if (is_not_tuple(Src) || *(Eterm *)(tmp_arg1 = (Eterm) tuple_val(Src)) != Arity) { \
- Fail; \
- } \
- } while (0)
- #define IsBoolean(X, Fail) if ((X) != am_true && (X) != am_false) { Fail; }
- #define IsBinary(Src, Fail) \
- if (is_not_binary(Src) || binary_bitsize(Src) != 0) { Fail; }
- #define IsBitstring(Src, Fail) \
- if (is_not_binary(Src)) { Fail; }
- #ifdef ARCH_64
- #define BsSafeMul(A, B, Fail, Target) \
- do { Uint64 _res = (A) * (B); \
- if (_res / B != A) { Fail; } \
- Target = _res; \
- } while (0)
- #else
- #define BsSafeMul(A, B, Fail, Target) \
- do { Uint64 _res = (Uint64)(A) * (Uint64)(B); \
- if ((_res >> (8*sizeof(Uint))) != 0) { Fail; } \
- Target = _res; \
- } while (0)
- #endif
- #define BsGetFieldSize(Bits, Unit, Fail, Target) \
- do { \
- Sint _signed_size; Uint _uint_size; \
- if (is_small(Bits)) { \
- _signed_size = signed_val(Bits); \
- if (_signed_size < 0) { Fail; } \
- _uint_size = (Uint) _signed_size; \
- } else { \
- if (!term_to_Uint(Bits, &temp_bits)) { Fail; } \
- _uint_size = temp_bits; \
- } \
- BsSafeMul(_uint_size, Unit, Fail, Target); \
- } while (0)
- #define BsGetUncheckedFieldSize(Bits, Unit, Fail, Target) \
- do { \
- Sint _signed_size; Uint _uint_size; \
- if (is_small(Bits)) { \
- _signed_size = signed_val(Bits); \
- if (_signed_size < 0) { Fail; } \
- _uint_size = (Uint) _signed_size; \
- } else { \
- if (!term_to_Uint(Bits, &temp_bits)) { Fail; } \
- _uint_size = (Uint) temp_bits; \
- } \
- Target = _uint_size * Unit; \
- } while (0)
- #define BsGetFloat2(Ms, Live, Sz, Flags, Dst, Store, Fail) \
- do { \
- ErlBinMatchBuffer *_mb; \
- Eterm _result; Sint _size; \
- if (!is_small(Sz) || (_size = unsigned_val(Sz)) > 64) { Fail; } \
- _size *= ((Flags) >> 3); \
- TestHeap(FLOAT_SIZE_OBJECT, Live); \
- _mb = ms_matchbuffer(Ms); \
- SWAPOUT; \
- _result = erts_bs_get_float_2(c_p, _size, (Flags), _mb); \
- HTOP = HEAP_TOP(c_p); \
- if (is_non_value(_result)) { Fail; } \
- else { Store(_result, Dst); } \
- } while (0)
- #define BsGetBinaryImm_2(Ms, Live, Sz, Flags, Dst, Store, Fail) \
- do { \
- ErlBinMatchBuffer *_mb; \
- Eterm _result; \
- TestHeap(heap_bin_size(ERL_ONHEAP_BIN_LIMIT), Live); \
- _mb = ms_matchbuffer(Ms); \
- SWAPOUT; \
- _result = erts_bs_get_binary_2(c_p, (Sz), (Flags), _mb); \
- HTOP = HEAP_TOP(c_p); \
- if (is_non_value(_result)) { Fail; } \
- else { Store(_result, Dst); } \
- } while (0)
- #define BsGetBinary_2(Ms, Live, Sz, Flags, Dst, Store, Fail) \
- do { \
- ErlBinMatchBuffer *_mb; \
- Eterm _result; Uint _size; \
- BsGetFieldSize(Sz, ((Flags) >> 3), Fail, _size); \
- TestHeap(ERL_SUB_BIN_SIZE, Live); \
- _mb = ms_matchbuffer(Ms); \
- SWAPOUT; \
- _result = erts_bs_get_binary_2(c_p, _size, (Flags), _mb); \
- HTOP = HEAP_TOP(c_p); \
- if (is_non_value(_result)) { Fail; } \
- else { Store(_result, Dst); } \
- } while (0)
- #define BsGetBinaryAll_2(Ms, Live, Unit, Dst, Store, Fail) \
- do { \
- ErlBinMatchBuffer *_mb; \
- Eterm _result; \
- TestHeap(ERL_SUB_BIN_SIZE, Live); \
- _mb = ms_matchbuffer(Ms); \
- if (((_mb->size - _mb->offset) % Unit) == 0) { \
- SWAPOUT; \
- _result = erts_bs_get_binary_all_2(c_p, _mb); \
- HTOP = HEAP_TOP(c_p); \
- ASSERT(is_value(_result)); \
- Store(_result, Dst); \
- } else { Fail; } \
- } while (0)
- #define BsSkipBits2(Ms, Bits, Unit, Fail) \
- do { \
- ErlBinMatchBuffer *_mb; \
- size_t new_offset; \
- Uint _size; \
- _mb = ms_matchbuffer(Ms); \
- BsGetFieldSize(Bits, Unit, Fail, _size); \
- new_offset = _mb->offset + _size; \
- if (new_offset <= _mb->size) { _mb->offset = new_offset; } \
- else { Fail; } \
- } while (0)
- #define BsSkipBitsAll2(Ms, Unit, Fail) \
- do { \
- ErlBinMatchBuffer *_mb; \
- _mb = ms_matchbuffer(Ms); \
- if (((_mb->size - _mb->offset) % Unit) == 0) {_mb->offset = _mb->size; } \
- else { Fail; } \
- } while (0)
- #define BsSkipBitsImm2(Ms, Bits, Fail) \
- do { \
- ErlBinMatchBuffer *_mb; \
- size_t new_offset; \
- _mb = ms_matchbuffer(Ms); \
- new_offset = _mb->offset + (Bits); \
- if (new_offset <= _mb->size) { _mb->offset = new_offset; } \
- else { Fail; } \
- } while (0)
- #define NewBsPutIntegerImm(Sz, Flags, Src) \
- do { \
- if (!erts_new_bs_put_integer(ERL_BITS_ARGS_3((Src), (Sz), (Flags)))) { goto badarg; } \
- } while (0)
- #define NewBsPutInteger(Sz, Flags, Src) \
- do { \
- Sint _size; \
- BsGetUncheckedFieldSize(Sz, ((Flags) >> 3), goto badarg, _size); \
- if (!erts_new_bs_put_integer(ERL_BITS_ARGS_3((Src), _size, (Flags)))) \
- { goto badarg; } \
- } while (0)
- #define NewBsPutFloatImm(Sz, Flags, Src) \
- do { \
- if (!erts_new_bs_put_float(c_p, (Src), (Sz), (Flags))) { goto badarg; } \
- } while (0)
- #define NewBsPutFloat(Sz, Flags, Src) \
- do { \
- Sint _size; \
- BsGetUncheckedFieldSize(Sz, ((Flags) >> 3), goto badarg, _size); \
- if (!erts_new_bs_put_float(c_p, (Src), _size, (Flags))) { goto badarg; } \
- } while (0)
- #define NewBsPutBinary(Sz, Flags, Src) \
- do { \
- Sint _size; \
- BsGetUncheckedFieldSize(Sz, ((Flags) >> 3), goto badarg, _size); \
- if (!erts_new_bs_put_binary(ERL_BITS_ARGS_2((Src), _size))) { goto badarg; } \
- } while (0)
- #define NewBsPutBinaryImm(Sz, Src) \
- do { \
- if (!erts_new_bs_put_binary(ERL_BITS_ARGS_2((Src), (Sz)))) { goto badarg; } \
- } while (0)
- #define NewBsPutBinaryAll(Src, Unit) \
- do { \
- if (!erts_new_bs_put_binary_all(ERL_BITS_ARGS_2((Src), (Unit)))) { goto badarg; } \
- } while (0)
- #define IsPort(Src, Fail) if (is_not_port(Src)) { Fail; }
- #define IsPid(Src, Fail) if (is_not_pid(Src)) { Fail; }
- #define IsRef(Src, Fail) if (is_not_ref(Src)) { Fail; }
- static BifFunction translate_gc_bif(void* gcf);
- static Eterm* handle_error(Process* c_p, Eterm* pc, Eterm* reg, BifFunction bf);
- static Eterm* next_catch(Process* c_p, Eterm *reg);
- static void terminate_proc(Process* c_p, Eterm Value);
- static Eterm add_stacktrace(Process* c_p, Eterm Value, Eterm exc);
- static void save_stacktrace(Process* c_p, Eterm* pc, Eterm* reg,
- BifFunction bf, Eterm args);
- static struct StackTrace * get_trace_from_exc(Eterm exc);
- static Eterm make_arglist(Process* c_p, Eterm* reg, int a);
- static Eterm call_error_handler(Process* p, Eterm* ip, Eterm* reg);
- static Eterm call_breakpoint_handler(Process* p, Eterm* fi, Eterm* reg);
- static Uint* fixed_apply(Process* p, Eterm* reg, Uint arity);
- static Eterm* apply(Process* p, Eterm module, Eterm function,
- Eterm args, Eterm* reg);
- static int hibernate(Process* c_p, Eterm module, Eterm function,
- Eterm args, Eterm* reg);
- static Eterm* call_fun(Process* p, int arity, Eterm* reg, Eterm args);
- static Eterm* apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg);
- static Eterm new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free);
- #if defined(_OSE_) || defined(VXWORKS)
- static int init_done;
- #endif
- void
- init_emulator(void)
- {
- #if defined(_OSE_) || defined(VXWORKS)
- init_done = 0;
- #endif
- process_main();
- }
- /*
- * On certain platforms, make sure that the main variables really are placed
- * in registers.
- */
- #if defined(__GNUC__) && defined(sparc) && !defined(DEBUG)
- # define REG_x0 asm("%l0")
- # define REG_xregs asm("%l1")
- # define REG_htop asm("%l2")
- # define REG_stop asm("%l3")
- # define REG_I asm("%l4")
- # define REG_fcalls asm("%l5")
- # define REG_tmp_arg1 asm("%l6")
- # define REG_tmp_arg2 asm("%l7")
- #else
- # define REG_x0
- # define REG_xregs
- # define REG_htop
- # define REG_stop
- # define REG_I
- # define REG_fcalls
- # define REG_tmp_arg1
- # define REG_tmp_arg2
- #endif
- /*
- * process_main() is called twice:
- * The first call performs some initialisation, including exporting
- * the instructions' C labels to the loader.
- * The second call starts execution of BEAM code. This call never returns.
- */
- void process_main(void)
- {
- #if !defined(_OSE_) && !defined(VXWORKS)
- static int init_done = 0;
- #endif
- Process* c_p = NULL;
- int reds_used;
- /*
- * X register zero; also called r(0)
- */
- register Eterm x0 REG_x0 = NIL;
- /* Pointer to X registers: x(1)..x(N); reg[0] is used when doing GC,
- * in all other cases x0 is used.
- */
- register Eterm* reg REG_xregs = NULL;
- /*
- * Top of heap (next free location); grows upwards.
- */
- register Eterm* HTOP REG_htop = NULL;
- #ifdef HYBRID
- Eterm *g_htop;
- Eterm *g_hend;
- #endif
- /* Stack pointer. Grows downwards; points
- * to last item pushed (normally a saved
- * continuation pointer).
- */
- register Eterm* E REG_stop = NULL;
- /*
- * Pointer to next threaded instruction.
- */
- register Eterm *I REG_I = NULL;
- /* Number of reductions left. This function
- * returns to the scheduler when FCALLS reaches zero.
- */
- register Sint FCALLS REG_fcalls = 0;
- /*
- * Temporaries used for picking up arguments for instructions.
- */
- register Eterm tmp_arg1 REG_tmp_arg1 = NIL;
- register Eterm tmp_arg2 REG_tmp_arg2 = NIL;
- Eterm tmp_big[2]; /* Temporary buffer for small bignums. */
- #ifndef ERTS_SMP
- static Eterm save_reg[ERTS_X_REGS_ALLOCATED];
- /* X registers -- not used directly, but
- * through 'reg', because using it directly
- * needs two instructions on a SPARC,
- * while using it through reg needs only
- * one.
- */
- /*
- * Floating point registers.
- */
- static FloatDef freg[MAX_REG];
- #else
- /* X regisers and floating point registers are located in
- * scheduler specific data.
- */
- register FloatDef *freg;
- #endif
- /*
- * For keeping the negative old value of 'reds' when call saving is active.
- */
- int neg_o_reds = 0;
- Eterm (*arith_func)(Process* p, Eterm* reg, Uint live);
- #ifndef NO_JUMP_TABLE
- static void* opcodes[] = { DEFINE_OPCODES };
- #ifdef ERTS_OPCODE_COUNTER_SUPPORT
- static void* counting_opcodes[] = { DEFINE_COUNTING_OPCODES };
- #endif
- #else
- int Go;
- #endif
- Uint temp_bits; /* Temporary used by BsSkipBits2 & BsGetInteger2 */
- ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */
- /*
- * Note: In this function, we attempt to place rarely executed code towards
- * the end of the function, in the hope that the cache hit rate will be better.
- * The initialization code is only run once, so it is at the very end.
- *
- * Note: c_p->arity must be set to reflect the number of useful terms in
- * c_p->arg_reg before calling the scheduler.
- */
- if (!init_done) {
- init_done = 1;
- goto init_emulator;
- }
- #ifndef ERTS_SMP
- reg = save_reg; /* XXX: probably wastes a register on x86 */
- #endif
- c_p = NULL;
- reds_used = 0;
- goto do_schedule1;
- do_schedule:
- reds_used = REDS_IN(c_p) - FCALLS;
- do_schedule1:
- PROCESS_MAIN_CHK_LOCKS(c_p);
- ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
- c_p = schedule(c_p, reds_used);
- ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- #ifdef ERTS_SMP
- reg = c_p->scheduler_data->save_reg;
- freg = c_p->scheduler_data->freg;
- #endif
- ERL_BITS_RELOAD_STATEP(c_p);
- {
- int reds;
- Eterm* argp;
- Eterm* next;
- int i;
- argp = c_p->arg_reg;
- for (i = c_p->arity - 1; i > 0; i--) {
- reg[i] = argp[i];
- CHECK_TERM(reg[i]);
- }
- /*
- * We put the original reduction count in the process structure, to reduce
- * the code size (referencing a field in a struct through a pointer stored
- * in a register gives smaller code than referencing a global variable).
- */
- SET_I(c_p->i);
- reds = c_p->fcalls;
- if (c_p->ct != NULL && (c_p->trace_flags & F_SENSITIVE) == 0) {
- neg_o_reds = -reds;
- FCALLS = REDS_IN(c_p) = 0;
- } else {
- neg_o_reds = 0;
- FCALLS = REDS_IN(c_p) = reds;
- }
- next = (Eterm *) *I;
- r(0) = c_p->arg_reg[0];
- #ifdef HARDDEBUG
- if (c_p->arity > 0) {
- CHECK_TERM(r(0));
- }
- #endif
- SWAPIN;
- ASSERT(VALID_INSTR(next));
- Goto(next);
- }
- #if defined(DEBUG) || defined(NO_JUMP_TABLE)
- emulator_loop:
- #endif
- #ifdef NO_JUMP_TABLE
- switch (Go) {
- #endif
- #include "beam_hot.h"
- #define STORE_ARITH_RESULT(res) StoreBifResult(2, (res));
- #define ARITH_FUNC(name) erts_gc_##name
- OpCase(i_plus_jId):
- {
- Eterm result;
- if (is_both_small(tmp_arg1, tmp_arg2)) {
- Sint i = signed_val(tmp_arg1) + signed_val(tmp_arg2);
- ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
- if (MY_IS_SSMALL(i)) {
- result = make_small(i);
- STORE_ARITH_RESULT(result);
- }
-
- }
- arith_func = ARITH_FUNC(mixed_plus);
- goto do_big_arith2;
- }
- OpCase(i_minus_jId):
- {
- Eterm result;
- if (is_both_small(tmp_arg1, tmp_arg2)) {
- Sint i = signed_val(tmp_arg1) - signed_val(tmp_arg2);
- ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
- if (MY_IS_SSMALL(i)) {
- result = make_small(i);
- STORE_ARITH_RESULT(result);
- }
- }
- arith_func = ARITH_FUNC(mixed_minus);
- goto do_big_arith2;
- }
- OpCase(i_is_lt_f):
- if (CMP_GE(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
- OpCase(i_is_ge_f):
- if (CMP_LT(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
- OpCase(i_is_eq_f):
- if (CMP_NE(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
- OpCase(i_is_ne_f):
- if (CMP_EQ(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
- OpCase(i_is_eq_exact_f):
- if (!EQ(tmp_arg1, tmp_arg2)) {
- ClauseFail();
- }
- Next(1);
- OpCase(i_move_call_only_fcr): {
- r(0) = Arg(1);
- }
- /* FALL THROUGH */
- OpCase(i_call_only_f): {
- SET_I((Eterm *) Arg(0));
- Dispatch();
- }
- OpCase(i_move_call_last_fPcr): {
- r(0) = Arg(2);
- }
- /* FALL THROUGH */
- OpCase(i_call_last_fP): {
- RESTORE_CP(E);
- E = ADD_BYTE_OFFSET(E, Arg(1));
- SET_I((Eterm *) Arg(0));
- Dispatch();
- }
- OpCase(i_move_call_crf): {
- r(0) = Arg(0);
- I++;
- }
- /* FALL THROUGH */
- OpCase(i_call_f): {
- SET_CP(c_p, I+2);
- SET_I((Eterm *) Arg(0));
- Dispatch();
- }
- OpCase(i_move_call_ext_last_ePcr): {
- r(0) = Arg(2);
- }
- /* FALL THROUGH */
- OpCase(i_call_ext_last_eP):
- RESTORE_CP(E);
- E = ADD_BYTE_OFFSET(E, Arg(1));
- /*
- * Note: The pointer to the export entry is never NULL; if the module
- * is not loaded, it points to code which will invoke the error handler
- * (see lb_call_error_handler below).
- */
- Dispatchx();
- OpCase(i_move_call_ext_cre): {
- r(0) = Arg(0);
- I++;
- }
- /* FALL THROUGH */
- OpCase(i_call_ext_e):
- SET_CP(c_p, I+2);
- Dispatchx();
- OpCase(i_move_call_ext_only_ecr): {
- r(0) = Arg(1);
- }
- /* FALL THROUGH */
- OpCase(i_call_ext_only_e):
- Dispatchx();
- OpCase(init_y): {
- Eterm* next;
- PreFetch(1, next);
- make_blank(yb(Arg(0)));
- NextPF(1, next);
- }
- OpCase(i_trim_I): {
- Eterm* next;
- Uint words;
- Uint cp;
- words = Arg(0);
- cp = E[0];
- PreFetch(1, next);
- E += words;
- E[0] = cp;
- NextPF(1, next);
- }
- OpCase(return):
- SET_I(c_p->cp);
- CHECK_TERM(r(0));
- Goto(*I);
- OpCase(test_heap_1_put_list_Iy): {
- Eterm* next;
- PreFetch(2, next);
- TestHeap(Arg(0), 1);
- PutList(yb(Arg(1)), r(0), r(0), StoreSimpleDest);
- CHECK_TERM(r(0));
- NextPF(2, next);
- }
- OpCase(put_string_IId):
- {
- unsigned char* s;
- int len;
- Eterm result;
- len = Arg(0); /* Length. */
- result = NIL;
- for (s = (unsigned char *) Arg(1); len > 0; s--, len--) {
- PutList(make_small(*s), result, result, StoreSimpleDest);
- }
- StoreBifResult(2, result);
- }
- /*
- * Send is almost a standard call-BIF with two arguments, except for:
- * 1) It cannot be traced.
- * 2) There is no pointer to the send_2 function stored in
- * the instruction.
- */
- OpCase(send): {
- Eterm* next;
- Eterm result;
- PRE_BIF_SWAPOUT(c_p);
- c_p->fcalls = FCALLS - 1;
- result = send_2(c_p, r(0), x(1));
- PreFetch(0, next);
- POST_BIF_GC_SWAPIN(c_p, result, reg, 2);
- FCALLS = c_p->fcalls;
- if (is_value(result)) {
- r(0) = result;
- CHECK_TERM(r(0));
- NextPF(0, next);
- } else if (c_p->freason == RESCHEDULE) {
- Eterm* argp;
- c_p->arity = 2;
- /*
- * Moving c_p->arg to a register is shorter than using c_p->arg_reg
- * directly, since c_p->arg_reg is a pointer (not an array)
- * and the compiler generates code to fetch the pointer every time.
- */
- argp = c_p->arg_reg;
- argp[0] = r(0);
- argp[1] = x(1);
- SWAPOUT;
- c_p->i = I;
- c_p->current = NULL;
- goto do_schedule;
- } else if (c_p->freason == TRAP) {
- SET_CP(c_p, I+1);
- SET_I(((Export *)(c_p->def_arg_reg[3]))->address);
- SWAPIN;
- r(0) = c_p->def_arg_reg[0];
- x(1) = c_p->def_arg_reg[1];
- Dispatch();
- }
- goto find_func_info;
- }
- OpCase(i_element_jssd): {
- Eterm index;
- Eterm tuple;
- /*
- * Inlined version of element/2 for speed.
- */
- GetArg2(1, index, tuple);
- if (is_small(index) && is_tuple(tuple)) {
- Eterm* tp = tuple_val(tuple);
- if ((signed_val(index) >= 1) &&
- (signed_val(index) <= arityval(*tp))) {
- Eterm result = tp[signed_val(index)];
- StoreBifResult(3, result);
- }
- }
- }
- /* Fall through */
- OpCase(badarg_j):
- badarg:
- c_p->freason = BADARG;
- goto lb_Cl_error;
- OpCase(i_fast_element_jIsd): {
- Eterm tuple;
- /*
- * Inlined version of element/2 for even more speed.
- * The first argument is an untagged integer >= 1.
- * The second argument is guaranteed to be a register operand.
- */
- GetArg1(2, tuple);
- if (is_tuple(tuple)) {
- Eterm* tp = tuple_val(tuple);
- tmp_arg2 = Arg(1);
- if (tmp_arg2 <= arityval(*tp)) {
- Eterm result = tp[tmp_arg2];
- StoreBifResult(3, result);
- }
- }
- goto badarg;
- }
- OpCase(catch_yf):
- c_p->catches++;
- yb(Arg(0)) = Arg(1);
- Next(2);
- OpCase(catch_end_y): {
- c_p->catches--;
- make_blank(yb(Arg(0)));
- if (is_non_value(r(0))) {
- if (x(1) == am_throw) {
- r(0) = x(2);
- } else {
- if (x(1) == am_error) {
- SWAPOUT;
- x(2) = add_stacktrace(c_p, x(2), x(3));
- SWAPIN;
- }
- /* only x(2) is included in the rootset here */
- if (E - HTOP < 3 || c_p->mbuf) { /* Force GC in case add_stacktrace()
- * created heap fragments */
- SWAPOUT;
- PROCESS_MAIN_CHK_LOCKS(c_p);
- FCALLS -= erts_garbage_collect(c_p, 3, reg+2, 1);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- SWAPIN;
- }
- r(0) = TUPLE2(HTOP, am_EXIT, x(2));
- HTOP += 3;
- }
- }
- CHECK_TERM(r(0));
- Next(1);
- }
- OpCase(try_end_y): {
- c_p->catches--;
- make_blank(yb(Arg(0)));
- if (is_non_value(r(0))) {
- r(0) = x(1);
- x(1) = x(2);
- x(2) = x(3);
- }
- Next(1);
- }
- /*
- * Skeleton for receive statement:
- *
- * L1: <-------------------+
- * <-----------+ |
- * | |
- * loop_rec L2 ------+---+ |
- * ... | | |
- * remove_message | | |
- * jump L3 | | |
- * ... | | |
- * loop_rec_end L1 --+ | |
- * L2: <---------------+ |
- * wait L1 -----------------+ or wait_timeout
- * timeout
- *
- * L3: Code after receive...
- *
- *
- */
- /*
- * Pick up the next message and place it in x(0).
- * If no message, jump to a wait or wait_timeout instruction.
- */
- OpCase(i_loop_rec_fr):
- {
- Eterm* next;
- ErlMessage* msgp;
- loop_rec__:
- PROCESS_MAIN_CHK_LOCKS(c_p);
- msgp = PEEK_MESSAGE(c_p);
- if (!msgp) {
- #ifdef ERTS_SMP
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- /* Make sure messages wont pass exit signals... */
- if (ERTS_PROC_PENDING_EXIT(c_p)) {
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- SWAPOUT;
- goto do_schedule; /* Will be rescheduled for exit */
- }
- ERTS_SMP_MSGQ_MV_INQ2PRIVQ(c_p);
- msgp = PEEK_MESSAGE(c_p);
- if (msgp)
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- else {
- #endif
- SET_I((Eterm *) Arg(0));
- Goto(*I); /* Jump to a wait or wait_timeout instruction */
- #ifdef ERTS_SMP
- }
- #endif
- }
- MV_MSG_MBUF_INTO_PROC(msgp);
- PreFetch(1, next);
- r(0) = ERL_MESSAGE_TERM(msgp);
- NextPF(1, next);
- }
- /*
- * Remove a (matched) message from the message queue.
- */
- OpCase(remove_message): {
- Eterm* next;
- ErlMessage* msgp;
- PROCESS_MAIN_CHK_LOCKS(c_p);
- PreFetch(0, next);
- msgp = PEEK_MESSAGE(c_p);
- if (c_p->ct != NULL) {
- save_calls(c_p, &exp_receive);
- }
- if (ERL_MESSAGE_TOKEN(msgp) == NIL) {
- SEQ_TRACE_TOKEN(c_p) = NIL;
- } else if (ERL_MESSAGE_TOKEN(msgp) != am_undefined) {
- Eterm msg;
- SEQ_TRACE_TOKEN(c_p) = ERL_MESSAGE_TOKEN(msgp);
- ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p)));
- ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5);
- ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p)));
- ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p)));
- ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p)));
- ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p)));
- c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
- if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) {
- c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
- }
- msg = ERL_MESSAGE_TERM(msgp);
- seq_trace_output(SEQ_TRACE_TOKEN(c_p), msg, SEQ_TRACE_RECEIVE,
- c_p->id, c_p);
- }
- UNLINK_MESSAGE(c_p, msgp);
- JOIN_MESSAGE(c_p);
- CANCEL_TIMER(c_p);
- free_message(msgp);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- NextPF(0, next);
- }
- /*
- * Advance the save pointer to the next message (the current
- * message didn't match), then jump to the loop_rec instruction.
- */
- OpCase(loop_rec_end_f): {
- SET_I((Eterm *) Arg(0));
- SAVE_MESSAGE(c_p);
- goto loop_rec__;
- }
- /*
- * Prepare to wait for a message or a timeout, whichever occurs first.
- *
- * Note: In order to keep the compatibility between 32 and 64 bits
- * emulators, only timeout values that can be represented in 32 bits
- * (unsigned) or less are allowed.
- */
- OpCase(i_wait_timeout_fs): {
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- /* Fall through */
- }
- OpCase(i_wait_timeout_locked_fs): {
- Eterm timeout_value;
- /*
- * If we have already set the timer, we must NOT set it again. Therefore,
- * we must test the F_INSLPQUEUE flag as well as the F_TIMO flag.
- */
- if (c_p->flags & (F_INSLPQUEUE | F_TIMO)) {
- goto wait2;
- }
- GetArg1(1, timeout_value);
- if (timeout_value != make_small(0)) {
- #if !defined(ARCH_64)
- Uint time_val;
- #endif
- if (is_small(timeout_value) && signed_val(timeout_value) > 0 &&
- #if defined(ARCH_64)
- ((unsigned_val(timeout_value) >> 32) == 0)
- #else
- 1
- #endif
- ) {
- /*
- * The timer routiner will set c_p->i to the value in
- * c_p->def_arg_reg[0]. Note that it is safe to use this
- * location because there are no living x registers in
- * a receive statement.
- */
- c_p->def_arg_reg[0] = (Eterm) (I+3);
- set_timer(c_p, unsigned_val(timeout_value));
- } else if (timeout_value == am_infinity) {
- c_p->flags |= F_TIMO;
- #if !defined(ARCH_64)
- } else if (term_to_Uint(timeout_value, &time_val)) {
- c_p->def_arg_reg[0] = (Eterm) (I+3);
- set_timer(c_p, time_val);
- #endif
- } else { /* Wrong time */
- OpCase(i_wait_error_locked): {
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- /* Fall through */
- }
- OpCase(i_wait_error): {
- c_p->freason = EXC_TIMEOUT_VALUE;
- goto find_func_info;
- }
- }
- /*
- * Prepare to wait indefinitely for a new message to arrive
- * (or the time set above if falling through from above).
- *
- * When a new message arrives, control will be transferred
- * the loop_rec instruction (at label L1). In case of
- * of timeout, control will be transferred to the timeout
- * instruction following the wait_timeout instruction.
- */
- OpCase(wait_locked_f):
- OpCase(wait_f):
- wait2: {
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
- c_p->i = (Eterm *) Arg(0); /* L1 */
- SWAPOUT;
- c_p->arity = 0;
- c_p->status = P_WAITING;
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- c_p->current = NULL;
- goto do_schedule;
- }
- OpCase(wait_unlocked_f): {
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- goto wait2;
- }
- }
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- Next(2);
- }
- OpCase(i_wait_timeout_fI): {
- erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- }
- OpCase(i_wait_timeout_locked_fI):
- {
- /*
- * If we have already set the timer, we must NOT set it again. Therefore,
- * we must test the F_INSLPQUEUE flag as well as the F_TIMO flag.
- */
- if ((c_p->flags & (F_INSLPQUEUE | F_TIMO)) == 0) {
- c_p->def_arg_reg[0] = (Eterm) (I+3);
- set_timer(c_p, Arg(1));
- }
- goto wait2;
- }
- /*
- * A timeout has occurred. Reset the save pointer so that the next
- * receive statement will examine the first message first.
- */
- OpCase(timeout_locked): {
- erts_smp_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE);
- }
- OpCase(timeout): {
- Eterm* next;
- PreFetch(0, next);
- if (IS_TRACED_FL(c_p, F_TRACE_RECEIVE)) {
- trace_receive(c_p, am_timeout);
- }
- if (c_p->ct != NULL) {
- save_calls(c_p, &exp_timeout);
- }
- c_p->flags &= ~F_TIMO;
- JOIN_MESSAGE(c_p);
- NextPF(0, next);
- }
- OpCase(i_select_val_sfI):
- GetArg1(0, tmp_arg1);
- do_binary_search:
- {
- struct Pairs {
- Eterm val;
- Eterm* addr;
- };
- struct Pairs* low;
- struct Pairs* high;
- struct Pairs* mid;
- int bdiff; /* int not long because the arrays aren't that large */
- low = (struct Pairs *) &Arg(3);
- high = low + Arg(2);
- /* The pointer subtraction (high-low) below must produce
- * a signed result, because high could be < low. That
- * requires the compiler to insert quite a bit of code.
- *
- * However, high will be > low so the result will be
- * positive. We can use that knowledge to optimise the
- * entire sequence, from the initial com…
Large files files are truncated, but you can click here to view the full file