aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/beam/instrs.tab
blob: d45da62d03255d4979feea79053cafcdb38aa8a9 (plain) (tree)




















                                                                           
                                  
 

                              

 

                                          

 










                                       
     

 






                                               

 






















                                                                         


                    
                    
                





                                                    

                                   



                          

                                   


                                   

                             







                                           
                         

 
                                 



                            


                                       
 





                                                 

 


                                   

 


                            

 



































                                                                      
     
                          

 




                                        
     
                          

 







                           
 




                                                          

 






























































                                           
                




































                                                                  




























































                                                            

















































                                                                                     




































                                                              




















































































                                     


                                     
                            







                                 
                          















                                                

































































                                                                  



                 

                      
                 
























































































































                                                                  
 

















                                 





                                           





                         





                                           















                                        
























































                                                                                
     
                     

 








                             
 























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

// Stack manipulation instructions

allocate(NeedStack, Live) {
    $AH($NeedStack, 0, $Live);
}

allocate_heap(NeedStack, NeedHeap, Live) {
    $AH($NeedStack, $NeedHeap, $Live);
}

allocate_init(NeedStack, Live, Y) {
    $AH($NeedStack, 0, $Live);
    make_blank($Y);
}

allocate_zero(NeedStack, Live) {
    Eterm* ptr;
    int i = $NeedStack;
    $AH(i, 0, $Live);
    for (ptr = E + i; ptr > E; ptr--) {
	 make_blank(*ptr);
    }
}

allocate_heap_zero(NeedStack, NeedHeap, Live) {
    Eterm* ptr;
    int i = $NeedStack;
    $AH(i, $NeedHeap, $Live);
    for (ptr = E + i; ptr > E; ptr--) {
        make_blank(*ptr);
    }
}

// This instruction is probably never used (because it is combined with a
// a return). However, a future compiler might for some reason emit a
// deallocate not followed by a return, and that should work.

deallocate(Deallocate) {
    //| -no_prefetch
    SET_CP(c_p, (BeamInstr *) cp_val(*E));
    E = ADD_BYTE_OFFSET(E, $Deallocate);
}

deallocate_return(Deallocate) {
    //| -no_next
    int words_to_pop = $Deallocate;
    SET_I((BeamInstr *) cp_val(*E));
    E = ADD_BYTE_OFFSET(E, words_to_pop);
    CHECK_TERM(x(0));
    DispatchReturn;
}

move_deallocate_return(Src, Deallocate) {
    x(0) = $Src;
    $deallocate_return($Deallocate);
}

// Call instructions

DISPATCH(CallDest) {
    //| -no_next
    SET_I((BeamInstr *) $CallDest);
    DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
    Dispatch();
}

i_call(CallDest) {
    SET_CP(c_p, $NEXT_INSTRUCTION);
    $DISPATCH($CallDest);
}

move_call(Src, CallDest) {
    x(0) = $Src;
    SET_CP(c_p, $NEXT_INSTRUCTION);
    $DISPATCH($CallDest);
}

i_call_last(CallDest, Deallocate) {
    $deallocate($Deallocate);
    $DISPATCH($CallDest);
}

move_call_last(Src, CallDest, Deallocate) {
    x(0) = $Src;
    $i_call_last($CallDest, $Deallocate);
}

i_call_only(CallDest) {
    $DISPATCH($CallDest);
}

i_move_call_only(CallDest, Src) {
    x(0) = $Src;
    $i_call_only($CallDest);
}

move_call_only(Src, CallDest) {
    $i_move_call_only($CallDest, $Src);
}

DISPATCHX(Dest) {
    //| -no_next
    DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, $Dest);
    // Dispatchx assumes the Export* is in Arg(0)
    I = (&$Dest) - 1;
    Dispatchx();
}

i_call_ext(Dest) {
    SET_CP(c_p, $NEXT_INSTRUCTION);
    $DISPATCHX($Dest);
}

i_move_call_ext(Src, Dest) {
    x(0) = $Src;
    $i_call_ext($Dest);
}

i_call_ext_only(Dest) {
    $DISPATCHX($Dest);
}

i_move_call_ext_only(Dest, Src) {
    x(0) = $Src;
    $i_call_ext_only($Dest);
}

i_call_ext_last(Dest, Deallocate) {
    $deallocate($Deallocate);
    $DISPATCHX($Dest);
}

i_move_call_ext_last(Dest, StackOffset, Src) {
    x(0) = $Src;
    $i_call_ext_last($Dest, $StackOffset);
}

APPLY(I, Deallocate) {
    //| -no_next
    HEAVY_SWAPOUT;
    next = apply(c_p, r(0), x(1), x(2), reg, $I, $Deallocate);
    HEAVY_SWAPIN;
}

HANDLE_APPLY_ERROR() {
    I = handle_error(c_p, I, reg, &bif_export[BIF_apply_3]->info.mfa);
    goto post_error_handling;
}

i_apply() {
    BeamInstr *next;
    $APPLY(NULL, 0);
    if (next != NULL) {
        $i_call(next);
    }
    $HANDLE_APPLY_ERROR();
}

i_apply_last(Deallocate) {
    BeamInstr *next;
    $APPLY(I, $Deallocate);
    if (next != NULL) {
        $i_call_last(next, $Deallocate);
    }
    $HANDLE_APPLY_ERROR();
}

i_apply_only() {
    BeamInstr *next;
    $APPLY(I, 0);
    if (next != NULL) {
        $i_call_only(next);
    }
    $HANDLE_APPLY_ERROR();
}

FIXED_APPLY(Arity, I, Deallocate) {
    //| -no_next
    HEAVY_SWAPOUT;
    next = fixed_apply(c_p, reg, $Arity, $I, $Deallocate);
    HEAVY_SWAPIN;
}

apply(Arity) {
    BeamInstr *next;
    $FIXED_APPLY($Arity, NULL, 0);
    if (next != NULL) {
        $i_call(next);
    }
    $HANDLE_APPLY_ERROR();
}

apply_last(Arity, Deallocate) {
    BeamInstr *next;
    $FIXED_APPLY($Arity, I, $Deallocate);
    if (next != NULL) {
        $i_call_last(next, $Deallocate);
    }
    $HANDLE_APPLY_ERROR();
}

APPLY_FUN() {
    HEAVY_SWAPOUT;
    next = apply_fun(c_p, r(0), x(1), reg);
    HEAVY_SWAPIN;
}

HANDLE_APPLY_FUN_ERROR() {
     goto find_func_info;
}

DISPATCH_FUN(I) {
    SET_I($I);
    Dispatchfun();
}

i_apply_fun() {
    BeamInstr *next;
    $APPLY_FUN();
    if (next != NULL) {
        SET_CP(c_p, $NEXT_INSTRUCTION);
        $DISPATCH_FUN(next);
    }
    $HANDLE_APPLY_FUN_ERROR();
}

i_apply_fun_last(Deallocate) {
    BeamInstr *next;
    $APPLY_FUN();
    if (next != NULL) {
        $deallocate($Deallocate);
        $DISPATCH_FUN(next);
    }
    $HANDLE_APPLY_FUN_ERROR();
}

i_apply_fun_only() {
    BeamInstr *next;
    $APPLY_FUN();
    if (next != NULL) {
        $DISPATCH_FUN(next);
    }
    $HANDLE_APPLY_FUN_ERROR();
}

CALL_FUN(Fun) {
    //| -no_next
    HEAVY_SWAPOUT;
    next = call_fun(c_p, $Fun, reg, THE_NON_VALUE);
    HEAVY_SWAPIN;
}

i_call_fun(Fun) {
    BeamInstr *next;
    $CALL_FUN($Fun);
    if (next != NULL) {
        SET_CP(c_p, $NEXT_INSTRUCTION);
        $DISPATCH_FUN(next);
    }
    $HANDLE_APPLY_FUN_ERROR();
}

i_call_fun_last(Fun, Deallocate) {
    BeamInstr *next;
    $CALL_FUN($Fun);
    if (next != NULL) {
        $deallocate($Deallocate);
        $DISPATCH_FUN(next);
    }
    $HANDLE_APPLY_FUN_ERROR();
}

return() {
    SET_I(c_p->cp);
    DTRACE_RETURN_FROM_PC(c_p);

    /*
     * 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);
    DispatchReturn;
}

get_list(Src, Hd, Tl) {
    Eterm* tmp_ptr = list_val($Src);
    Eterm hd, tl;
    hd = CAR(tmp_ptr);
    tl = CDR(tmp_ptr);
    $Hd = hd;
    $Tl = tl;
}

i_get(Src, Dst) {
    $Dst = erts_pd_hash_get(c_p, $Src);
}

i_get_hash(Src, Hash, Dst) {
    $Dst = erts_pd_hash_get_with_hx(c_p, $Hash, $Src);
}

i_get_tuple_element(Src, Element, Dst) {
    Eterm* src = ADD_BYTE_OFFSET(tuple_val($Src), $Element);
    $Dst = *src;
}

i_get_tuple_element2(Src, Element, Dst) {
    Eterm* src;
    Eterm* dst;
    Eterm E1, E2;
    src = ADD_BYTE_OFFSET(tuple_val($Src), $Element);
    dst = &($Dst);
    E1 = src[0];
    E2 = src[1];
    dst[0] = E1;
    dst[1] = E2;
}

i_get_tuple_element2y(Src, Element, D1, D2) {
    Eterm* src;
    Eterm E1, E2;
    src = ADD_BYTE_OFFSET(tuple_val($Src), $Element);
    E1 = src[0];
    E2 = src[1];
    $D1 = E1;
    $D2 = E2;
}

i_get_tuple_element3(Src, Element, Dst) {
    Eterm* src;
    Eterm* dst;
    Eterm E1, E2, E3;
    src = ADD_BYTE_OFFSET(tuple_val($Src), $Element);
    dst = &($Dst);
    E1 = src[0];
    E2 = src[1];
    E3 = src[2];
    dst[0] = E1;
    dst[1] = E2;
    dst[2] = E3;
}

i_element := element_group.fetch.execute;


element_group.head() {
    Eterm element_index;
    Eterm element_tuple;
}

element_group.fetch(Src) {
    element_tuple = $Src;
}

element_group.execute(Fail, Index, Dst) {
    element_index = $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))) {
            $Dst = tp[signed_val(element_index)];
            $NEXT0();
        }
    }
    c_p->freason = BADARG;
    $BIF_ERROR_ARITY_2($Fail, BIF_element_2, element_index, element_tuple);
}

i_fast_element := fast_element_group.fetch.execute;

fast_element_group.head() {
    Eterm fast_element_tuple;
}

fast_element_group.fetch(Src) {
    fast_element_tuple = $Src;
}

fast_element_group.execute(Fail, Index, Dst) {
    if (is_tuple(fast_element_tuple)) {
        Eterm* tp = tuple_val(fast_element_tuple);
        Eterm pos = $Index;	/* Untagged integer >= 1 */
        if (pos <= arityval(*tp)) {
            $Dst = tp[pos];
            $NEXT0();
	}
    }
    c_p->freason = BADARG;
    $BIF_ERROR_ARITY_2($Fail, BIF_element_2, make_small($Index), fast_element_tuple);
}

init(Y) {
    make_blank($Y);
}

init2(Y1, Y2) {
    make_blank($Y1);
    make_blank($Y2);
}

init3(Y1, Y2, Y3) {
    make_blank($Y1);
    make_blank($Y2);
    make_blank($Y3);
}

i_make_fun(FunP, NumFree) {
    HEAVY_SWAPOUT;
    x(0) = new_fun(c_p, reg, (ErlFunEntry *) $FunP, $NumFree);
    HEAVY_SWAPIN;
}

i_trim(Words) {
    Uint cp = E[0];
    E += $Words;
    E[0] = cp;
}

move(Src, Dst) {
    $Dst = $Src;
}

move3(S1, D1, S2, D2, S3, D3) {
    $D1 = $S1;
    $D2 = $S2;
    $D3 = $S3;
}

move_dup(Src, D1, D2) {
    $D1 = $D2 = $Src;
}

move2_par(S1, D1, S2, D2) {
    Eterm V1, V2;
    V1 = $S1;
    V2 = $S2;
    $D1 = V1;
    $D2 = V2;
}

move_shift(Src, SD, D) {
    Eterm V;
    V = $Src;
    $D = $SD;
    $SD = V;
}

move_window3(S1, S2, S3, D) {
    Eterm xt0, xt1, xt2;
    Eterm* y = &$D;
    xt0  = $S1;
    xt1  = $S2;
    xt2  = $S3;
    y[0] = xt0;
    y[1] = xt1;
    y[2] = xt2;
}

move_window4(S1, S2, S3, S4, D) {
    Eterm xt0, xt1, xt2, xt3;
    Eterm* y = &$D;
    xt0  = $S1;
    xt1  = $S2;
    xt2  = $S3;
    xt3  = $S4;
    y[0] = xt0;
    y[1] = xt1;
    y[2] = xt2;
    y[3] = xt3;
}

move_window5(S1, S2, S3, S4, S5, D) {
    Eterm xt0, xt1, xt2, xt3, xt4;
    Eterm *y = &$D;
    xt0  = $S1;
    xt1  = $S2;
    xt2  = $S3;
    xt3  = $S4;
    xt4  = $S5;
    y[0] = xt0;
    y[1] = xt1;
    y[2] = xt2;
    y[3] = xt3;
    y[4] = xt4;
}

move_return(Src) {
    //| -no_next
    x(0) = $Src;
    SET_I(c_p->cp);
    c_p->cp = 0;
    DispatchReturn;
}

move_x1(Src) {
    x(1) = $Src;
}

move_x2(Src) {
    x(2) = $Src;
}

node(Dst) {
    $Dst = erts_this_node->sysname;
}

put_list(Hd, Tl, Dst) {
    HTOP[0] = $Hd;
    HTOP[1] = $Tl;
    $Dst = make_list(HTOP);
    HTOP += 2;
}

i_put_tuple := i_put_tuple.make.fill;

i_put_tuple.make(Dst) {
    $Dst = make_tuple(HTOP);
}

i_put_tuple.fill(Arity) {
    Eterm* hp = HTOP;
    Eterm arity = $Arity;

    //| -no_next
    *hp++ = make_arityval(arity);
    I = $NEXT_INSTRUCTION;
    do {
        Eterm term = *I++;
        switch (loader_tag(term)) {
        case LOADER_X_REG:
            *hp++ = x(loader_x_reg_index(term));
            break;
        case LOADER_Y_REG:
            *hp++ = y(loader_y_reg_index(term));
            break;
        default:
            *hp++ = term;
            break;
        }
    } while (--arity != 0);
    HTOP = hp;
    Goto(*I);
}

self(Dst) {
    $Dst = c_p->common.id;
}

set_tuple_element(Element, Tuple, Offset) {
    Eterm* p;

    ASSERT(is_tuple($Tuple));
    p = (Eterm *) ((unsigned char *) tuple_val($Tuple) + $Offset);
    *p = $Element;
}

swap(R1, R2) {
    Eterm V = $R1;
    $R1 = $R2;
    $R2 = V;
}

swap_temp(R1, R2, Tmp) {
    Eterm V = $R1;
    $R1 = $R2;
    $R2 = $Tmp = V;
}

test_heap(Nh, Live) {
    $GC_TEST(0, $Nh, $Live);
}

test_heap_1_put_list(Nh, Reg) {
    $test_heap($Nh, 1);
    $put_list($Reg, x(0), x(0));
}

is_integer_allocate(Fail, Src, NeedStack, Live) {
    //| -no_prefetch
    $is_integer($Fail, $Src);
    $AH($NeedStack, 0, $Live);
}

is_nonempty_list(Fail, Src) {
    //| -no_prefetch
    if (is_not_list($Src)) {
        $FAIL($Fail);
    }
}

is_nonempty_list_test_heap(Fail, Need, Live) {
    //| -no_prefetch
    $is_nonempty_list($Fail, x(0));
    $test_heap($Need, $Live);
}

is_nonempty_list_allocate(Fail, Src, Need, Live) {
    //| -no_prefetch
    $is_nonempty_list($Fail, $Src);
    $AH($Need, 0, $Live);
}

is_nonempty_list_get_list(Fail, Src, Hd, Tl) {
    //| -no_prefetch
    $is_nonempty_list($Fail, $Src);
    $get_list($Src, $Hd, $Tl);
}

jump(Fail) {
    $JUMP($Fail);
}

move_jump(Fail, Src) {
    x(0) = $Src;
    $jump($Fail);
}

//
// Test instructions.
//

is_atom(Fail, Src) {
    if (is_not_atom($Src)) {
        $FAIL($Fail);
    }
}

is_boolean(Fail, Src) {
    if (($Src) != am_true && ($Src) != am_false) {
        $FAIL($Fail);
    }
}

is_binary(Fail, Src) {
    if (is_not_binary($Src) || binary_bitsize($Src) != 0) {
        $FAIL($Fail);
    }
}

is_bitstring(Fail, Src) {
  if (is_not_binary($Src)) {
        $FAIL($Fail);
    }
}

is_float(Fail, Src) {
    if (is_not_float($Src)) {
        $FAIL($Fail);
    }
}

is_function(Fail, Src) {
    if ( !(is_any_fun($Src)) ) {
        $FAIL($Fail);
    }
}

is_function2(Fail, Fun, Arity) {
    if (erl_is_function(c_p, $Fun, $Arity) != am_true ) {
        $FAIL($Fail);
    }
}

is_integer(Fail, Src) {
    if (is_not_integer($Src)) {
        $FAIL($Fail);
    }
}

is_list(Fail, Src) {
    if (is_not_list($Src) && is_not_nil($Src)) {
        $FAIL($Fail);
    }
}

is_map(Fail, Src) {
    if (is_not_map($Src)) {
        $FAIL($Fail);
    }
}

is_nil(Fail, Src) {
    if (is_not_nil($Src)) {
        $FAIL($Fail);
    }
}

is_number(Fail, Src) {
    if (is_not_integer($Src) && is_not_float($Src)) {
        $FAIL($Fail);
    }
}

is_pid(Fail, Src) {
    if (is_not_pid($Src)) {
        $FAIL($Fail);
    }
}

is_port(Fail, Src) {
    if (is_not_port($Src)) {
        $FAIL($Fail);
    }
}

is_reference(Fail, Src) {
    if (is_not_ref($Src)) {
        $FAIL($Fail);
    }
}

is_tagged_tuple(Fail, Src, Arityval, Tag) {
    if (!(BEAM_IS_TUPLE($Src) &&
          (tuple_val($Src))[0] == $Arityval &&
          (tuple_val($Src))[1] == $Tag)) {
        $FAIL($Fail);
    }
}

is_tuple(Fail, Src) {
    if (is_not_tuple($Src)) {
        $FAIL($Fail);
    }
}

is_tuple_of_arity(Fail, Src, Arityval) {
    if (!(BEAM_IS_TUPLE($Src) && *tuple_val($Src) == $Arityval)) {
        $FAIL($Fail);
    }
}

test_arity(Fail, Pointer, Arity) {
    if (*tuple_val($Pointer) != $Arity) {
        $FAIL($Fail);
    }
}

i_is_eq_exact_immed(Fail, X, Y) {
    if ($X != $Y) {
        $FAIL($Fail);
    }
}

i_is_ne_exact_immed(Fail, X, Y) {
    if ($X == $Y) {
        $FAIL($Fail);
    }
}

is_eq_exact(Fail, X, Y) {
    if (!EQ($X, $Y)) {
        $FAIL($Fail);
    }
}

i_is_eq_exact_literal(Fail, Src, Literal) {
    if (!eq($Src, $Literal)) {
        $FAIL($Fail);
    }
}

is_ne_exact(Fail, X, Y) {
    if (EQ($X, $Y)) {
        $FAIL($Fail);
    }
}

i_is_ne_exact_literal(Fail, Src, Literal) {
    if (eq($Src, $Literal)) {
        $FAIL($Fail);
    }
}

is_eq(Fail, X, Y) {
    CMP_EQ_ACTION($X, $Y, $FAIL($Fail));
}

is_ne(Fail, X, Y) {
    CMP_NE_ACTION($X, $Y, $FAIL($Fail));
}

is_lt(Fail, X, Y) {
    CMP_LT_ACTION($X, $Y, $FAIL($Fail));
}

is_ge(Fail, X, Y) {
    CMP_GE_ACTION($X, $Y, $FAIL($Fail));
}

badarg(Fail) {
    $BADARG($Fail);
}

badmatch(Src) {
    c_p->fvalue = $Src;
    c_p->freason = BADMATCH;
    goto find_func_info;
}

case_end(Src) {
    c_p->fvalue = $Src;
    c_p->freason = EXC_CASE_CLAUSE;
    goto find_func_info;
}

if_end() {
    c_p->freason = EXC_IF_CLAUSE;
    goto find_func_info;
    //| -no_next;
}

system_limit(Fail) {
    $SYSTEM_LIMIT($Fail);
    //| -no_next;
}

catch(Y, Fail) {
    c_p->catches++;
    $Y = $Fail;
}

catch_end(Y) {
    c_p->catches--;
    make_blank($Y);
    if (is_non_value(r(0))) {
        c_p->fvalue = NIL;
        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) {
                SWAPOUT;
                PROCESS_MAIN_CHK_LOCKS(c_p);
                FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1, FCALLS);
                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));
}

try_end(Y) {
    c_p->catches--;
    make_blank($Y);
    if (is_non_value(r(0))) {
        c_p->fvalue = NIL;
        r(0) = x(1);
        x(1) = x(2);
        x(2) = x(3);
    }
}

try_case_end(Src) {
    c_p->fvalue = $Src;
    c_p->freason = EXC_TRY_CLAUSE;
    goto find_func_info;
    //| -no_next;
}

i_raise() {
    Eterm raise_trace = x(2);
    Eterm raise_value = x(1);
    struct StackTrace *s;

    c_p->fvalue = raise_value;
    c_p->ftrace = raise_trace;
    s = get_trace_from_exc(raise_trace);
    if (s == NULL) {
        c_p->freason = EXC_ERROR;
    } else {
        c_p->freason = PRIMARY_EXCEPTION(s->freason);
    }
    goto find_func_info;
}