aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/beam/instrs.tab
blob: fc88cab22fa036dca53f2353b561ca0803b8fa18 (plain) (tree)
1
2
3
4
5



                   
                                                        















                                                                           
                                  
 

                              

 

                                          

 










                                       
     

 






                                               

 






















                                                                         


                    







                                                    
                





                                                    
                                   
                             



                          
                                   
                             


                                   
                             
                             







                                           
                             

 
                               



                            





                                                 

 


                                   

 


                            

 


















                                              
                            

                  
                                             









                                                                      
                          
                                    

                                       
     
                          

 

                          
                                 
                                    

                                 
     
                          

 

                    
                       
                                    
                            


                          
 
                                         

                  
                                                           
                 

 

                    
                                        
                                    

                                       





                               
                                               
                                    

                                 



                          
                 
                  
                                            







                          
                





                    
                     
                                    







                                       
                     
                                    







                                 
                     
                                    




                              
                     
                
                  
                                                    




                    
                          
                                    







                                       
                          
                                    






                                 
                










                                                                  











                                    









                                    
























                                                            
                                                






















                                                     



                                         







                                         
                                 
                                                                          






















                                                                           
                                                    










                                                                                     



                   























                       
































                                                              




















































































                                     






                           
                        


                         






                                                     
                
                       
                                 
                          













                                                
                                
              
                                      
             















































                                                                  











                                                  











                                        



                 

                      
                 









































                                                           
                                     




                                                         





                                      
















































                                                     



                                              










                                        

                                                                  








                                         
 









                                                              

















                                 
                                           

                                              



                     





                         
                                           

                                              



                     















                                        

                   
                 





                            
                 





                                   
                 


















                                 
                 





















                                                                                
     
                     

 


                   








                               
 





















                                                     
                

 




                                       

             
                    






















                                                   
// -*- c -*-
//
// %CopyrightBegin%
//
// Copyright Ericsson AB 2017-2018. 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_REL(CallDest) {
    //| -no_next
    $SET_I_REL($CallDest);
    DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
    Dispatch();
}

DISPATCH_ABS(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_REL($CallDest);
}

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

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

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

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

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

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, Next) {
    //| -no_next
    HEAVY_SWAPOUT;
    $Next = apply(c_p, 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, next);
    if (ERTS_LIKELY(next != NULL)) {
        SET_CP(c_p, $NEXT_INSTRUCTION);
        $DISPATCH_ABS(next);
    }
    $HANDLE_APPLY_ERROR();
}

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

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

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

apply(Arity) {
    BeamInstr *next;
    $FIXED_APPLY($Arity, NULL, 0, next);
    if (ERTS_LIKELY(next != NULL)) {
        SET_CP(c_p, $NEXT_INSTRUCTION);
        $DISPATCH_ABS(next);
    }
    $HANDLE_APPLY_ERROR();
}

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

APPLY_FUN(Next) {
    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) {
    //| -no_next
    SET_I($I);
    Dispatchfun();
}

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

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

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

CALL_FUN(Fun, Next) {
    //| -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, next);
    if (ERTS_LIKELY(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, next);
    if (ERTS_LIKELY(next != NULL)) {
        $deallocate($Deallocate);
        $DISPATCH_FUN(next);
    }
    $HANDLE_APPLY_FUN_ERROR();
}

return() {
    //| -no_next
    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;
}

get_hd(Src, Hd) {
    Eterm* tmp_ptr = list_val($Src);
    $Hd = CAR(tmp_ptr);
}

get_tl(Src, Tl) {
    Eterm* tmp_ptr = list_val($Src);
    $Tl = CDR(tmp_ptr);
}

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_element2_dst(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_tuple;
}

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

element_group.execute(Fail, Index, Dst) {
    Eterm element_index = $Index;
    if (ERTS_LIKELY(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 (ERTS_LIKELY(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);
}

init_seq3(Y1) {
    Eterm* dst = &$Y1;
    make_blank(dst[0]);
    make_blank(dst[1]);
    make_blank(dst[2]);
}

init_seq4(Y1) {
    Eterm* dst = &$Y1;
    make_blank(dst[0]);
    make_blank(dst[1]);
    make_blank(dst[2]);
    make_blank(dst[3]);
}

init_seq5(Y1) {
    Eterm* dst = &$Y1;
    make_blank(dst[0]);
    make_blank(dst[1]);
    make_blank(dst[2]);
    make_blank(dst[3]);
    make_blank(dst[4]);
}

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;
}

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

put_tuple2(Dst, Arity) {
    Eterm* hp = HTOP;
    Eterm arity = $Arity;

    /*
     * If operands are not packed (in the 32-bit VM),
     * is is not safe to use $Dst directly after I
     * has been updated.
     */
    Eterm* dst_ptr = &($Dst);

    //| -no_next
    ASSERT(arity != 0);
    *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);
    *dst_ptr = make_tuple(HTOP);
    HTOP = hp;
    ASSERT(VALID_INSTR(* (Eterm *)I));
    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_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);
}

is_nonempty_list_get_hd(Fail, Src, Hd) {
    //| -no_prefetch
    $is_nonempty_list($Fail, $Src);
    $get_hd($Src, $Hd);
}

is_nonempty_list_get_tl(Fail, Src, Tl) {
    //| -no_prefetch
    $is_nonempty_list($Fail, $Src);
    $get_tl($Src, $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);
    }
}

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

hot_is_function2(Fail, Fun, Arity) {
    if (!is_function2($Fun, $Arity)) {
        $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) {
    Eterm term = $Src;
    if (!(BEAM_IS_TUPLE(term) &&
          (tuple_val(term))[0] == $Arityval &&
          (tuple_val(term))[1] == $Tag)) {
        $FAIL($Fail);
    }
}

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

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

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

test_arity_get_tuple_element(Fail, Pointer, Arity, Pos, Dst) {
    Eterm* ptr = tuple_val($Pointer);
    Eterm* src;
    if (*ptr != $Arity) {
        $FAIL($Fail);
    }
    src = ADD_BYTE_OFFSET(ptr, $Pos);
    $Dst = *src;
}

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) {
    Eterm src = $Src;
    if (is_immed(src) || !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) {
    Eterm src = $Src;
    if (!is_immed(src) && 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);
    //| -no_next;
}

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

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

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) {
    $try_end($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);
}

try_case(Y) {
    $try_end($Y);
    ASSERT(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;
    //| -no_next
}

build_stacktrace() {
    SWAPOUT;
    x(0) = build_stacktrace(c_p, x(0));
    SWAPIN;
}

raw_raise() {
    //| -no_prefetch
    Eterm class = x(0);
    Eterm value = x(1);
    Eterm stacktrace = x(2);

    if (class == am_error) {
        c_p->freason = EXC_ERROR & ~EXF_SAVETRACE;
        c_p->fvalue = value;
        c_p->ftrace = stacktrace;
        goto find_func_info;
    } else if (class == am_exit) {
        c_p->freason = EXC_EXIT & ~EXF_SAVETRACE;
        c_p->fvalue = value;
        c_p->ftrace = stacktrace;
        goto find_func_info;
    } else if (class == am_throw) {
        c_p->freason = EXC_THROWN & ~EXF_SAVETRACE;
        c_p->fvalue = value;
        c_p->ftrace = stacktrace;
        goto find_func_info;
    } else {
        x(0) = am_badarg;
    }
}