/erts/emulator/beam/beam_emu.c
C | 6589 lines | 4900 code | 730 blank | 959 comment | 905 complexity | 29d45437e2edcfc7ffe8a39bb9a27dfb MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-2-Clause
Large files files are truncated, but you can click here to view the full file
- /*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2010. 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%
- */
- #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_pix_lock_t *pix_lock__ = ERTS_PIX2PIXLOCK(internal_pid_index((P)->id));\
- erts_proc_lc_chk_only_proc_main((P)); \
- erts_pix_lock(pix_lock__); \
- ASSERT(0 < (P)->lock.refc && (P)->lock.refc < erts_no_schedulers*5);\
- erts_pix_unlock(pix_lock__); \
- } \
- 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
- /*
- * 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]))
- #define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
- /*
- * 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) \
- ((SWord)LabelAddr(emulator_loop) <= (SWord)(IP) && \
- (SWord)(IP) < (SWord)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 { \
- BeamInstr* stb_next; \
- Eterm stb_reg; \
- stb_reg = Arg(Dst); \
- I += (Dst) + 2; \
- stb_next = (BeamInstr *) *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) \
- do { \
- *(X) = make_cp(c_p->cp); \
- c_p->cp = 0; \
- } while(0)
- #define RESTORE_CP(X) SET_CP(c_p, (BeamInstr *) cp_val(*(X)))
- #define ISCATCHEND(instr) ((Eterm *) *(instr) == OpCode(catch_end_y))
- /*
- * Special Beam instructions.
- */
- BeamInstr beam_apply[2];
- BeamInstr beam_exit[1];
- BeamInstr beam_continue_exit[1];
- BeamInstr* em_call_error_handler;
- BeamInstr* em_apply_bif;
- BeamInstr* 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.
- */
- BeamInstr beam_return_to_trace[1]; /* OpCode(i_return_to_trace) */
- BeamInstr beam_return_trace[1]; /* OpCode(i_return_trace) */
- BeamInstr beam_exception_trace[1]; /* UGLY also OpCode(i_return_trace) */
- BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_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
- /*
- * Use LIGHT_SWAPOUT when the called function
- * will call HeapOnlyAlloc() (and never HAlloc()).
- */
- #ifdef DEBUG
- # /* The stack pointer is used in an assertion. */
- # define LIGHT_SWAPOUT SWAPOUT
- #else
- # define LIGHT_SWAPOUT HEAP_TOP(c_p) = HTOP
- #endif
- /*
- * Use LIGHT_SWAPIN when we know that c_p->stop cannot
- * have been updated (i.e. if there cannot have been
- * a garbage-collection).
- */
- #define LIGHT_SWAPIN HTOP = HEAP_TOP(c_p)
- #endif
- #ifdef FORCE_HEAP_FRAGS
- # define HEAP_SPACE_VERIFIED(Words) do { \
- c_p->space_verified = (Words); \
- c_p->space_verified_from = HTOP; \
- }while(0)
- #else
- # define HEAP_SPACE_VERIFIED(Words) ((void)0)
- #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) || (MSO(_p).overhead >= BIN_VHEAP_SZ(_p)) ) { \
- _res = erts_gc_after_bif_call((_p), (_res), NULL, 0); \
- } \
- SWAPIN
- # define POST_BIF_GC_SWAPIN(_p, _res, _regs, _arity) \
- if (((_p)->mbuf) || (MSO(_p).overhead >= BIN_VHEAP_SZ(_p)) ) { \
- _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)); \
- ERTS_VERIFY_UNUSED_TEMP_ALLOC((_p)); \
- if (((_p)->mbuf) || (MSO(_p).overhead >= BIN_VHEAP_SZ(_p)) ) { \
- _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_VERIFY_UNUSED_TEMP_ALLOC((_p)); \
- ERTS_SMP_REQ_PROC_MAIN_LOCK((_p)); \
- PROCESS_MAIN_CHK_LOCKS((_p)); \
- if (((_p)->mbuf) || (MSO(_p).overhead >= BIN_VHEAP_SZ(_p)) ) { \
- _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 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 Qb(N) (N)
- #define Ib(N) (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)); \
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
- 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;
- #define TestBinVHeap(VNh, Nh, Live) \
- do { \
- unsigned need = (Nh); \
- if ((E - HTOP < need) || (MSO(c_p).overhead + (VNh) >= BIN_VHEAP_SZ(c_p))) {\
- SWAPOUT; \
- reg[0] = r(0); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- FCALLS -= erts_garbage_collect(c_p, need, reg, (Live)); \
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- r(0) = reg[0]; \
- SWAPIN; \
- } \
- HEAP_SPACE_VERIFIED(need); \
- } 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.
- */
- #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)); \
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- r(0) = reg[0]; \
- SWAPIN; \
- } \
- HEAP_SPACE_VERIFIED(need); \
- } 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); \
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); \
- PROCESS_MAIN_CHK_LOCKS(c_p); \
- if (Live > 0) { \
- r(0) = reg[0]; \
- } \
- Extra = reg[Live]; \
- SWAPIN; \
- } \
- HEAP_SPACE_VERIFIED(need); \
- } while (0)
- #define TestHeapPutList(Need, Reg) \
- do { \
- TestHeap((Need), 1); \
- PutList(Reg, r(0), r(0), StoreSimpleDest); \
- CHECK_TERM(r(0)); \
- } 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)
- #define PutTuple(Dst, Arity) \
- do { \
- Dst = make_tuple(HTOP); \
- pt_arity = (Arity); \
- } 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 { \
- BeamInstr* dis_next; \
- dis_next = (BeamInstr *) *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch; \
- } \
- } while (0)
- #define DispatchMacroFun() \
- do { \
- BeamInstr* dis_next; \
- dis_next = (BeamInstr *) *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 (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) \
- && 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 = (BeamInstr *) *(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)); \
- c_p->cp = 0; \
- CHECK_TERM(r(0)); \
- Goto(*I)
- #define DeallocateReturn(Deallocate) \
- do { \
- int words_to_pop = (Deallocate); \
- SET_I((BeamInstr *) 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((BeamInstr *) CallDest); \
- Dispatch();
- #define MoveCallLast(Src, Dest, CallDest, Deallocate) \
- (Dest) = (Src); \
- RESTORE_CP(E); \
- E = ADD_BYTE_OFFSET(E, (Deallocate)); \
- SET_I((BeamInstr *) CallDest); \
- Dispatch();
- #define MoveCallOnly(Src, Dest, CallDest) \
- (Dest) = (Src); \
- SET_I((BeamInstr *) CallDest); \
- Dispatch();
- #define MoveJump(Src) \
- r(0) = (Src); \
- SET_I((BeamInstr *) Arg(0)); \
- Goto(*I);
- #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) COMPRESS_POINTER(((unsigned char *) tuple_val(Src)) + \
- (Element)); \
- (Dest) = (*(Eterm *) EXPAND_POINTER(tmp_arg1)); \
- } while (0)
- #define ExtractNextElement(Dest) \
- tmp_arg1 += sizeof(Eterm); \
- (Dest) = (* (Eterm *) (((unsigned char *) EXPAND_POINTER(tmp_arg1))))
- #define ExtractNextElement2(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \
- ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \
- tmp_arg1 += sizeof(Eterm) + sizeof(Eterm); \
- } while (0)
- #define ExtractNextElement3(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \
- ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \
- ene_dstp[2] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[3]; \
- tmp_arg1 += 3*sizeof(Eterm); \
- } while (0)
- #define ExtractNextElement4(Dest) \
- do { \
- Eterm* ene_dstp = &(Dest); \
- ene_dstp[0] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[1]; \
- ene_dstp[1] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[2]; \
- ene_dstp[2] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[3]; \
- ene_dstp[3] = ((Eterm *) EXPAND_POINTER(tmp_arg1))[4]; \
- tmp_arg1 += 4*sizeof(Eterm); \
- } while (0)
- #define ExtractElement(Element, Dest) \
- do { \
- tmp_arg1 += (Element); \
- (Dest) = (* (Eterm *) EXPAND_POINTER(tmp_arg1)); \
- } while (0)
- #define EqualImmed(X, Y, Action) if (X != Y) { Action; }
- #define NotEqualImmed(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 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 *) \
- EXPAND_POINTER(tmp_arg1 = (Eterm) \
- COMPRESS_POINTER(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 *) \
- EXPAND_POINTER(tmp_arg1 = \
- (Eterm) COMPRESS_POINTER(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; }
- #if defined(ARCH_64) && !HALFWORD_HEAP
- #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); \
- LIGHT_SWAPOUT; \
- _result = erts_bs_get_float_2(c_p, _size, (Flags), _mb); \
- LIGHT_SWAPIN; \
- HEAP_SPACE_VERIFIED(0); \
- 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); \
- LIGHT_SWAPOUT; \
- _result = erts_bs_get_binary_2(c_p, (Sz), (Flags), _mb); \
- LIGHT_SWAPIN; \
- HEAP_SPACE_VERIFIED(0); \
- 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); \
- LIGHT_SWAPOUT; \
- _result = erts_bs_get_binary_2(c_p, _size, (Flags), _mb); \
- LIGHT_SWAPIN; \
- HEAP_SPACE_VERIFIED(0); \
- 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) { \
- LIGHT_SWAPOUT; \
- _result = erts_bs_get_binary_all_2(c_p, _mb); \
- LIGHT_SWAPIN; \
- HEAP_SPACE_VERIFIED(0); \
- ASSERT(is_value(_result)); \
- Store(_result, Dst); \
- } else { \
- HEAP_SPACE_VERIFIED(0); \
- 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; }
- /*
- * process_main() is already huge, so we want to avoid inlining
- * into it. Especially functions that are seldom used.
- */
- #ifdef __GNUC__
- # define NOINLINE __attribute__((__noinline__))
- #else
- # define NOINLINE
- #endif
- /*
- * The following functions are called directly by process_main().
- * Don't inline them.
- */
- static BifFunction translate_gc_bif(void* gcf) NOINLINE;
- static BeamInstr* handle_error(Process* c_p, BeamInstr* pc,
- Eterm* reg, BifFunction bf) NOINLINE;
- static BeamInstr* call_error_handler(Process* p, BeamInstr* ip,
- Eterm* reg, Eterm func) NOINLINE;
- static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity) NOINLINE;
- static BeamInstr* apply(Process* p, Eterm module, Eterm function,
- Eterm args, Eterm* reg) NOINLINE;
- static int hibernate(Process* c_p, Eterm module, Eterm function,
- Eterm args, Eterm* reg) NOINLINE;
- static BeamInstr* call_fun(Process* p, int arity,
- Eterm* reg, Eterm args) NOINLINE;
- static BeamInstr* apply_fun(Process* p, Eterm fun,
- Eterm args, Eterm* reg) NOINLINE;
- static Eterm new_fun(Process* p, Eterm* reg,
- ErlFunEntry* fe, int num_free) NOINLINE;
- /*
- * Functions not directly called by process_main(). OK to inline.
- */
- static BeamInstr* 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, BeamInstr* 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);
- #if defined(VXWORKS)
- static int init_done;
- #endif
- void
- init_emulator(void)
- {
- #if 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(VXWORKS)
- static int init_done = 0;
- #endif
- Process* c_p = NULL;
- int reds_used;
- #ifdef DEBUG
- Eterm pid;
- #endif
- /*
- * 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 BeamInstr *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;
- #if HEAP_ON_C_STACK
- Eterm tmp_big[2]; /* Temporary buffer for small bignums if HEAP_ON_C_STACK. */
- #else
- Eterm *tmp_big; /* Temporary buffer for small bignums if !HEAP_ON_C_STACK. */
- #endif
- #ifndef ERTS_SMP
- #if !HALFWORD_HEAP
- 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.
- */
- #endif
- /*
- * 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 */
- Eterm pt_arity; /* Used by do_put_tuple */
- 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
- #if !HALFWORD_HEAP
- reg = save_reg; /* XXX: probably wastes a register on x86 */
- #else
- /* Registers need to be heap allocated (correct memory range) for tracing to work */
- reg = erts_alloc(ERTS_ALC_T_BEAM_REGISTER, ERTS_X_REGS_ALLOCATED * sizeof(Eterm));
- #endif
- #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);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- c_p = schedule(c_p, reds_used);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- #ifdef DEBUG
- pid = c_p->id;
- #endif
- 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
- #if !HEAP_ON_C_STACK
- tmp_big = ERTS_PROC_GET_SCHDATA(c_p)->beam_emu_tmp_heap;
- #endif
- ERL_BITS_RELOAD_STATEP(c_p);
- {
- int reds;
- Eterm* argp;
- BeamInstr *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 (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)
- && (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 = (BeamInstr *) *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
- {
- Eterm increment_reg_val;
- Eterm increment_val;
- Uint live;
- Eterm result;
- OpCase(i_increment_yIId):
- increment_reg_val = yb(Arg(0));
- goto do_increment;
- OpCase(i_increment_xIId):
- increment_reg_val = xb(Arg(0));
- goto do_increment;
- OpCase(i_increment_rIId):
- increment_reg_val = r(0);
- I--;
- do_increment:
- increment_val = Arg(1);
- if (is_small(increment_reg_val)) {
- Sint i = signed_val(increment_reg_val) + increment_val;
- ASSERT(MY_IS_SSMALL(i) == IS_SSMALL(i));
- if (MY_IS_SSMALL(i)) {
- result = make_small(i);
- store_result:
- StoreBifResult(3, result);
- }
- }
- live = Arg(2);
- SWAPOUT;
- reg[0] = r(0);
- reg[live] = increment_reg_val;
- reg[live+1] = make_small(increment_val);
- result = erts_gc_mixed_plus(c_p, reg, live);
- r(0) = reg[0];
- SWAPIN;
- ERTS_HOLE_CHECK(c_p);
- if (is_value(result)) {
- goto store_result;
- }
- ASSERT(c_p->freason != BADMATCH || is_value(c_p->fvalue));
- goto find_func_info;
- }
-
- 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);
- {
- Eterm is_eq_exact_lit_val;
- OpCase(i_is_eq_exact_literal_xfc):
- is_eq_exact_lit_val = xb(Arg(0));
- I++;
- goto do_is_eq_exact_literal;
- OpCase(i_is_eq_exact_literal_yfc):
- is_eq_exact_lit_val = yb(Arg(0));
- I++;
- goto do_is_eq_exact_literal;
- OpCase(i_is_eq_exact_literal_rfc):
- is_eq_exact_lit_val = r(0);
- do_is_eq_exact_literal:
- if (!eq(Arg(1), is_eq_exact_lit_val)) {
- ClauseFail();
- }
- Next(2);
- }
- {
- Eterm is_ne_exact_lit_val;
- OpCase(i_is_ne_exact_literal_xfc):
- is_ne_exact_lit_val = xb(Arg(0));
- I++;
- goto do_is_ne_exact_literal;
- OpCase(i_is_ne_exact_literal_yfc):
- is_ne_exact_lit_val = yb(Arg(0));
- I++;
- goto do_is_ne_exact_literal;
- OpCase(i_is_ne_exact_literal_rfc):
- is_ne_exact_lit_val = r(0);
- do_is_ne_exact_literal:
- if (eq(Arg(1), is_ne_exact_lit_val)) {
- ClauseFail();
- }
- Next(2);
- }
- OpCase(i_move_call_only_fcr): {
- r(0) = Arg(1);
- }
- /* FALL THROUGH */
- OpCase(i_call_only_f): {
- SET_I((BeamInstr *) 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((BeamInstr *) 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((BeamInstr *) 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): {
- BeamInstr *next;
- PreFetch(1, next);
- make_blank(yb(Arg(0)));
- NextPF(1, next);
- }
- OpCase(i_trim_I): {
- BeamInstr *next;
- Uint words;
- Uint cp;
- words = Arg(0);
- cp = E[0];
- PreFetch(1, next);
- E += words;
- E[0] = cp;
- NextPF(1, next);
- }
- OpCase(move_x1_c): {
- x(1) = Arg(0);
- Next(1);
- }
- OpCase(move_x2_c): {
- x(2) = Arg(0);
- Next(1);
- }
- OpCase(return): {
- SET_I(c_p->cp);
- /*
- * We must clear the CP to make sure that a stale value do not
- * create a false module dependcy preventing code upgrading.
- * It also means that we can use the CP in stack backtraces.
- */
- c_p->cp = 0;
- CHECK_TERM(r(0));
- HEAP_SPACE_VERIFIED(0);
- Goto(*I);
- }
- /*
- * 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): {
- BeamInstr *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 == TRAP) {
- SET_CP(c_p, I+1);
- SET_I(*((BeamInstr **) (BeamInstr) ((c_p)->def_arg_reg + 3)));
- SWAPIN;
- r(0) = c_p->def_arg_reg[0];
- x(1) = c_p->def_arg_reg[1];
- Dispatch();
- }
- goto find_func_info;
- }
- {
- Eterm element_index;
- Eterm element_tuple;
- OpCase(i_element_xjsd):
- element_tuple = xb(Arg(0));
- I++;
- goto do_element;
- OpCase(i_element_yjsd):
- element_tuple = yb(Arg(0));
- I++;
- goto do_element;
- OpCase(i_element_rjsd):
- element_tuple = r(0);
- /* Fall through */
- do_element:
- GetArg1(1, element_index);
- if (is_small(element_index) && is_tuple(element_tuple)) {
- Eterm* tp = tuple_val(element_tuple);
- if ((signed_val(element_index) >= 1) &&
- (signed_val(element_index) <= arityval(*tp))) {
- Eterm result = tp[signed_val(element_index)];
- StoreBifResult(2, result);
- }
- }
- }
- /* Fall through */
- OpCase(badarg_j):
- badarg:
- c_p->freason = BADARG;
- goto lb_Cl_error;
- {
- Eterm fast_element_tuple;
- OpCase(i_fast_element_rjId):
- fast_element_tuple = r(0);
- do_fast_element:
- if (is_tuple(fast_element_tuple)) {
- Eterm* tp = tuple_val(fast_element_tuple);
- Eterm pos = Arg(1); /* Untagged integer >= 1 */
- if (pos <= arityval(*tp)) {
- Eterm result = tp[pos];
- StoreBifResult(2, result);
- }
- }
- goto badarg;
- OpCase(i_fast_element_xjId):
- fast_element_tuple = xb(Arg(0));
- I++;
- goto do_fast_element;
- OpCase(i_fast_element_yjId):
- fast_element_tuple = yb(Arg(0));
- I++;
- goto do_fast_element;
- }
- 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);
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- 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:
- *
- * recv_mark L1 Optional
- * call make_ref/monitor Optional
- * ...
- * recv_set L1 Optional
- * L1: <-------------------+
- * <-----------+ |
- * | |
- * loop_rec L2 ------+---+ |
- * ... | | |
- * remove_message | | |
- * jump L3 | | |
- * ... | | |
- * loop_rec_end L1 --+ | |
- * L2: <---------------+ |
- * wait L1 -----------------+ or wait_timeout
- * timeout
- *
- * L3: Code after receive...
- *
- *
- */
- OpCase(recv_mark_f): {
- /*
- * Save the current position in message buffer and the
- * the label for the loop_rec/2 instruction for the
- * the receive statement.
- */
- c_p->msg.mark = (BeamInstr *) Arg(0);
- c_p->msg.saved_last = c_p->msg.last;
- Next(1);
- }
- OpCase(i_recv_set): {
- /*
- * If the mark is valid (points to the loop_rec/2
- * instruction that follows), we know that the saved
- * position points to the first message that could
- * possibly be matched out.
- *
- * If the mark is invalid, we do nothing, meaning that
- * we will look through all messages in the message queue.
- */
- if (c_p->msg.mark == (BeamInstr *) (I+1)) {
- c_p->msg.save = c_p->msg.saved_last;
- }
- I++;
- /* Fall through to the loop_rec/2 instruction */
- }
- /*
- * Pick up the n…
Large files files are truncated, but you can click here to view the full file