// -*- 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%
//

return_trace() {
    ErtsCodeMFA* mfa = (ErtsCodeMFA *)(E[0]);

    SWAPOUT;		/* Needed for shared heap */
    ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
    erts_trace_return(c_p, mfa, r(0), ERTS_TRACER_FROM_ETERM(E+1)/* tracer */);
    ERTS_REQ_PROC_MAIN_LOCK(c_p);
    SWAPIN;
    c_p->cp = NULL;
    SET_I((BeamInstr *) cp_val(E[2]));
    E += 3;
    Goto(*I);
    //| -no_next
}

i_generic_breakpoint() {
    BeamInstr real_I;
    HEAVY_SWAPOUT;
    real_I = erts_generic_breakpoint(c_p, erts_code_to_codeinfo(I), reg);
    HEAVY_SWAPIN;
    ASSERT(VALID_INSTR(real_I));
    Goto(real_I);
    //| -no_next
}

i_return_time_trace() {
    BeamInstr *pc = (BeamInstr *) (UWord) E[0];
    SWAPOUT;
    erts_trace_time_return(c_p, erts_code_to_codeinfo(pc));
    SWAPIN;
    c_p->cp = NULL;
    SET_I((BeamInstr *) cp_val(E[1]));
    E += 2;
    Goto(*I);
    //| -no_next
}

i_return_to_trace() {
    if (IS_TRACED_FL(c_p, F_TRACE_RETURN_TO)) {
        Uint *cpp = (Uint*) E;
        for(;;) {
            ASSERT(is_CP(*cpp));
            if (*cp_val(*cpp) == (BeamInstr) OpCode(return_trace)) {
                do
                    ++cpp;
                while (is_not_CP(*cpp));
                cpp += 2;
            } else if (*cp_val(*cpp) == (BeamInstr) OpCode(i_return_to_trace)) {
                do
                    ++cpp;
                while (is_not_CP(*cpp));
            } else {
                break;
            }
        }
        SWAPOUT;		/* Needed for shared heap */
        ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
        erts_trace_return_to(c_p, cp_val(*cpp));
        ERTS_REQ_PROC_MAIN_LOCK(c_p);
        SWAPIN;
    }
    c_p->cp = NULL;
    SET_I((BeamInstr *) cp_val(E[0]));
    E += 1;
    Goto(*I);
    //| -no_next
}

i_yield() {
    /* This is safe as long as REDS_IN(c_p) is never stored
     * in c_p->arg_reg[0]. It is currently stored in c_p->def_arg_reg[5].
     */
    c_p->arg_reg[0] = am_true;
    c_p->arity = 1; /* One living register (the 'true' return value) */
    SWAPOUT;
    c_p->i = $NEXT_INSTRUCTION;
    c_p->current = NULL;
    goto do_schedule;
    //| -no_next
}

i_hibernate() {
    HEAVY_SWAPOUT;
    if (erts_hibernate(c_p, r(0), x(1), x(2), reg)) {
        FCALLS = c_p->fcalls;
        c_p->flags &= ~F_HIBERNATE_SCHED;
        goto do_schedule;
    } else {
        HEAVY_SWAPIN;
        I = handle_error(c_p, I, reg, &bif_export[BIF_hibernate_3]->info.mfa);
        goto post_error_handling;
    }
    //| -no_next
}

// This is optimised as an instruction because
// it has to be very very fast.

i_perf_counter() {
    ErtsSysPerfCounter ts;

    ts = erts_sys_perf_counter();
    if (IS_SSMALL(ts)) {
        r(0) = make_small((Sint)ts);
    } else {
        $GC_TEST(0, ERTS_SINT64_HEAP_SIZE(ts), 0);
        r(0) = make_big(HTOP);
#if defined(ARCH_32)
        if (ts >= (((Uint64) 1) << 32)) {
            *HTOP = make_pos_bignum_header(2);
            BIG_DIGIT(HTOP, 0) = (Uint) (ts & ((Uint) 0xffffffff));
            BIG_DIGIT(HTOP, 1) = (Uint) ((ts >> 32) & ((Uint) 0xffffffff));
            HTOP += 3;
        }
        else
#endif
            {
                *HTOP = make_pos_bignum_header(1);
                BIG_DIGIT(HTOP, 0) = (Uint) ts;
                HTOP += 2;
            }
    }
}

i_debug_breakpoint() {
    HEAVY_SWAPOUT;
    I = call_error_handler(c_p, erts_code_to_codemfa(I), reg, am_breakpoint);
    HEAVY_SWAPIN;
    if (I) {
        Goto(*I);
    }
    goto handle_error;
    //| -no_next
}