// -*- c -*- // // %CopyrightBegin% // // Copyright Ericsson AB 2017. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // %CopyrightEnd% // // // Define a regular expression that will match instructions that // perform GC. That will allow beam_makeops to check for instructions // that don't use $REFRESH_GEN_DEST() when they should. // GC_REGEXP=erts_garbage_collect|erts_gc|GcBifFunction; // $Offset is relative to the start of the instruction (not to the // location of the failure label reference). Since combined // instructions may increment the instruction pointer (e.g. in // 'increment') for some of the instructions in the group, we actually // use a virtual start position common to all instructions in the // group. To calculate the correct virtual position, we will need to // add $IP_ADJUSTMENT to the offset. ($IP_ADJUSTMENT will usually be // zero, except in a few bit syntax instructions.) SET_I_REL(Offset) { ASSERT(VALID_INSTR(*(I + ($Offset) + $IP_ADJUSTMENT))); I += $Offset + $IP_ADJUSTMENT; } SET_CP_I_ABS(Target) { c_p->i = $Target; ASSERT(VALID_INSTR(*c_p->i)); } SET_REL_I(Dst, Offset) { $Dst = I + ($Offset); ASSERT(VALID_INSTR(*$Dst)); } FAIL(Fail) { //| -no_prefetch $SET_I_REL($Fail); Goto(*I); } JUMP(Fail) { //| -no_next $SET_I_REL($Fail); Goto(*I); } GC_SWAPOUT() { // // Since a garbage collection is expensive anyway, we can afford // to save the instruction counter so that the correct function will // be pointed in the crash dump if the garbage collection fails // because of insufficient memory. // SWAPOUT; c_p->i = I; } GC_TEST(Ns, Nh, Live) { Uint need = $Nh + $Ns; if (ERTS_UNLIKELY(E - HTOP < need)) { $GC_SWAPOUT(); PROCESS_MAIN_CHK_LOCKS(c_p); FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live, FCALLS); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); SWAPIN; } HEAP_SPACE_VERIFIED($Nh); } GC_TEST_PRESERVE(NeedHeap, Live, PreserveTerm) { Uint need = $NeedHeap; if (ERTS_UNLIKELY(E - HTOP < need)) { $GC_SWAPOUT(); reg[$Live] = $PreserveTerm; PROCESS_MAIN_CHK_LOCKS(c_p); FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live+1, FCALLS); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); $PreserveTerm = reg[$Live]; SWAPIN; } HEAP_SPACE_VERIFIED($NeedHeap); } // Make sure that there are NeedStack + NeedHeap + 1 words available // on the combined heap/stack segment, then decrement the stack // pointer by (NeedStack + 1) words. Finally clear the word reserved // for the continuation pointer at the top of the stack. // // Stack frame layout: // // +-----------+ // y(N) | Term | // +-----------+ // . // . // . // +-----------+ // y(0) | Term | // +-----------+ // E ==> | NIL or CP | // +-----------+ // // When the function owning the stack frame is the currently executing // function, the word at the top of the stack is NIL. When calling // another function, the continuation pointer will be stored in the // word at the top of the stack. When returning to the function // owning the stack frame, the word at the stack top will again be set // to NIL. AH(NeedStack, NeedHeap, Live) { unsigned needed = $NeedStack + 1; $GC_TEST(needed, $NeedHeap, $Live); E -= needed; *E = NIL; } // // Helpers for call instructions // DISPATCH() { BeamInstr dis_next; dis_next = *I; CHECK_ARGS(I); if (FCALLS > 0 || FCALLS > neg_o_reds) { FCALLS--; Goto(dis_next); } else { goto context_switch; } } DISPATCH_ABS(CallDest) { SET_I((BeamInstr *) $CallDest); DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I)); $DISPATCH(); } DISPATCH_EXPORT(Export) { BeamInstr dis_next; Export *ep; ep = (Export*)($Export); DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, ep); SET_I(ep->addressv[erts_active_code_ix()]); CHECK_ARGS(I); dis_next = *I; if (ERTS_UNLIKELY(FCALLS <= 0)) { if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) && FCALLS > neg_o_reds) { save_calls(c_p, ep); } else { goto context_switch; } } FCALLS--; Goto(dis_next); } DISPATCH_FUN(I) { BeamInstr dis_next; SET_I($I); dis_next = *I; CHECK_ARGS(I); if (FCALLS > 0 || FCALLS > neg_o_reds) { FCALLS--; Goto(dis_next); } else { goto context_switch_fun; } } DISPATCH_REL(CallDest) { $SET_I_REL($CallDest); DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I)); $DISPATCH(); } DISPATCH_RETURN() { if (FCALLS > 0 || FCALLS > neg_o_reds) { FCALLS--; Goto(*I); } else { c_p->current = NULL; c_p->arity = 1; goto context_switch3; } } // Save the continuation pointer in the reserved slot at the // top of the stack as preparation for doing a function call. SAVE_CONTINUATION_POINTER(IP) { ASSERT(VALID_INSTR(*($IP))); *E = (BeamInstr) ($IP); } // Return to the function whose continuation pointer is stored // at the top of the stack and set that word to NIL. RETURN() { SET_I(cp_val(*E)); *E = NIL; } NEXT0() { //| -no_next SET_I((BeamInstr *) $NEXT_INSTRUCTION); Goto(*I); } NEXT(Addr) { //| -no_next SET_I((BeamInstr *) $Addr); Goto(*I); } FAIL_BODY() { //| -no_prefetch goto find_func_info; } FAIL_HEAD_OR_BODY(Fail) { //| -no_prefetch /* * In a correctly working program, we expect failures in * guards to be more likely than failures in bodies. */ if (ERTS_LIKELY($Fail)) { $FAIL($Fail); } goto find_func_info; } BADARG(Fail) { c_p->freason = BADARG; $FAIL_HEAD_OR_BODY($Fail); } BADARITH0() { c_p->freason = BADARITH; goto find_func_info; } SYSTEM_LIMIT(Fail) { c_p->freason = SYSTEM_LIMIT; $FAIL_HEAD_OR_BODY($Fail); } BIF_ERROR_ARITY_1(Fail, BIF, Op1) { //| -no_prefetch if (ERTS_LIKELY($Fail)) { $FAIL($Fail); } reg[0] = $Op1; SWAPOUT; I = handle_error(c_p, I, reg, &bif_trap_export[$BIF].info.mfa); goto post_error_handling; } BIF_ERROR_ARITY_2(Fail, BIF, Op1, Op2) { //| -no_prefetch if (ERTS_LIKELY($Fail)) { $FAIL($Fail); } reg[0] = $Op1; reg[1] = $Op2; SWAPOUT; I = handle_error(c_p, I, reg, &bif_trap_export[$BIF].info.mfa); goto post_error_handling; }