Here we will go through most of the instructions in the BEAM generic instruction set in detail. In the next section we list all instructions with a brief explanation generated from the documentaion in the code (see lib/compiler/src/genop.tab).
Instruction number 1 in the generic instruction set is not really an instruction at all. It is just a module local label giving a name, or actually a number to the current position in the code.
Each label potentially marks the beginning of a basic block since it is a potential destination of a jump.
The code for each function starts with a func_info instruction. This instruction is used for generating a function clause error, and the execution of the code in the function actually starts at the label following the func_info instruction.
Imagine a function with a guard:
id(I) when is_integer(I) -> I.
The Beam code for this function might look like:
{function, id, 1, 4}.
{label,3}.
{func_info,{atom,test1},{atom,id},1}.
{label,4}.
{test,is_integer,{f,3},[{x,0}]}.
return.
Here the meta information {function, id, 1, 4} tells us that execution of the id/1 function will start at label 4. At label 4 we do an is_integer on x0 and if we fail we jump to label 3 (f3) which points to the func_info instruction, which will generate a function clause exception. Otherwise we just fall through and return the argument (x0).
The type test instructions (is_\* Lbl Argument) checks whether the argument is of the given type and if not jumps to the label Lbl. The beam disassembler wraps all these instructions in a test instruction. E.g.:
{test,is_integer,{f,3},[{x,0}]}.
The current type test instructions are is_integer, is_float, is_number, is_atom, is_pid, is_reference, is_port, is_nil, is_binary, is_list, is_nonempty_list, is_function, is_function2, is_boolean, is_bitstr, and is_tuple.
And then there is also one type test instruction of Arity 3: test_arity Lbl Arg Arity. This instruction tests that the arity of the argument (assumed to be a tuple) is of Arity. This instruction is usually preceded by an is_tuple instruction.
The comparison instructions (is_\* Lbl Arg1 Arg2) compares the two arguments according to the instructions and jumps to Lbl if the comparison fails.
The comparison instructions are: : is_lt, is_ge, is_eq, is_ne, is_eq_exact, and is_ne_exact.
Remember that all Erlang terms are ordered so these instructions can compare any two terms. You can for example test if the atom self is less than the pid returned by self(). (It is.)
Note that for numbers the comparison is done on the Erlang type number, see [CH-TypeSystem]. That is, for a mixed float and integer comparison the number of lower precision is converted to the other type before comparison. For example on my system 1 and 0.1 compares as equal, as well as 9999999999999999 and 1.0e16. Comparing floating point numbers is always risk and best avoided, the result may wary depending on the underlying hardware.
If you want to make sure that the integer 1 and the floating point number 1.0 are compared different you can use is_eq_exact and is_ne_exact. This corresponds to the Erlang operators =:= and =/=.
In this chapter we will summarize what the different call instructions does. For a thorough description of how function calls work see [CH-Calls].
Does a call to the function of arity Arity in the same module at label Label. First count down the reductions and if needed do a context switch.
For all local calls the label is the second label of the function where the code starts. It is assumed that the preceding instruction at that label is func_info in order to get the MFA if a context switch is needed.
Do a tail recursive call the function of arity Arity in the same module at label Label. First count down the reductions and if needed do a context switch.
Deallocate Deallocate words of stack, then do a tail recursive call to the function of arity Arity in the same module at label Label First count down the reductions and if needed do a context switch.
Does an external call to the function of arity Arity given by Destination. Destination is usually of the form {extfunc, Module, Function, Arity}. First count down the reductions and if needed do a context switch.
Does a tail recursive external call to the function of arity Arity given by Destination. Destination is usually of the form {extfunc, Module, Function, Arity}. First count down the reductions and if needed do a context switch.
Deallocate Deallocate words of stack, then do a tail recursive external call to the function of arity Arity given by Destination. Destination is usually of the form {extfunc, Module, Function, Arity}. First count down the reductions and if needed do a context switch.
Call the bif Bif with the given arguments, and store the result in Reg. If the bif fails jump to Lbl. No zero arity bif can fail an thus those calls doesn’t take a fail label.
Call the bif Bif with the given arguments, and store the result in Reg. If the bif fails jump to Lbl. Store the arguments in x(Live), x(Live+1) and x(live+2).
The instruction call_fun assumes that the arguments are placed in the argument registers and that the fun (the pointer to the closure) is placed in the last argument register.
That is, for a zero arity call, the closure is in x0. For a arity 1 call x0 contains the argument and x1 contains the closure.
The stack and the heap of an Erlang process on Beam share the same memory area see [CH-Processes] and [CH-Memory] for a full discussion. The stack grows toward lower addresses and the heap toward higher addresses. Beam will do a garbage collection if more space than what is available is needed on either the stack or the heap.
- A leaf function
-
A leaf function is a function which doesn’t call any other function.
- A non leaf function
-
A non leaf function is a function which may call another function.
These instructions are also used by non leaf functions for setting up and tearing down the stack frame for the current instruction. That is, on entry to the function the continuation pointer (CP) is saved on the stack, and on exit it is read back from the stack.
A function skeleton for a leaf function looks like this:
{function, Name, Arity, StartLabel}. {label,L1}. {func_info,{atom,Module},{atom,Name},Arity}. {label,L2}. ... return.
A function skeleton for a non leaf function looks like this:
{function, Name, Arity, StartLabel}. {label,L1}. {func_info,{atom,Module},{atom,Name},Arity}. {label,L2}. {allocate,Need,Live}. ... call ... ... {deallocate,Need}. return.
Save the continuation pointer (CP) and allocate space for StackNeed extra words on the stack. If a GC is needed during allocation save Live number of X registers. E.g. if Live is 2 then registers X0 and X1 are saved.
When allocating on the stack, the stack pointer (E) is decreased.
Before After | xxx | | xxx | E -> | xxx | | xxx | | | | ??? | caller save slot ... E -> | CP | ... ... HTOP -> | | HTOP -> | | | xxx | | xxx |
Save the continuation pointer (CP) and allocate space for StackNeed extra words on the stack. Ensure that there also is space for HeapNeed words on the heap. If a GC is needed during allocation save Live number of X registers.
Note that the heap pointer (HTOP) is not changed until the actual heap allocation takes place.
This instruction works the same way as allocate, but it also clears out the allocated stack slots with NIL.
Before After | xxx | | xxx | E -> | xxx | | xxx | | | | NIL | caller save slot ... E -> | CP | ... ... HTOP -> | | HTOP -> | | | xxx | | xxx |
The allocate_heap_zero instruction works as the allocate_heap instruction, but it also clears out the allocated stack slots with NIL.
The test_heap instruction ensures there is space for HeapNeed words on the heap. If a GC is needed save Live number of X registers.
The deallocate instruction is the opposite of the allocate instruction, it restores the continuation pointer and deallocates N+1 stack words.
Removes N words of stack usage, while keeping the continuation pointer on the top of the stack. (The argument Remaining is to the best of my knowledge unused.)
Trim 2 Before After | ??? | | ??? | | xxx | E -> | CP | | xxx | | ... | E -> | CP | | ... | | | | ... | ... ... HTOP -> | | HTOP -> | | | xxx | | xxx |
Move the source Source (a litteral or a register) to the destination register Destination.
Get the head and tail (or car and cdr) parts of a list (a cons cell) from Source and put them into the registers Head and Tail.
Get element number Element from the tuple in Source and put it in the destination register Destination.
TODO ==== bs_put_binary/5 TODO ==== bs_put_float/5 TODO ==== bs_put_string/2 TODO ==== bs_init2/6 TODO ==== bs_add/5 TODO ==== bs_start_match2/5 TODO ==== bs_get_integer2/7 TODO ==== bs_get_float2/7 TODO ==== bs_get_binary2/7 TODO ==== bs_skip_bits2/5 TODO ==== bs_test_tail2/3 TODO ==== bs_save2/2 TODO ==== bs_restore2/2 TODO ==== bs_context_to_binary/1 TODO ==== bs_test_unit/3 TODO ==== bs_match_string/4 TODO ==== bs_init_writable/0 TODO ==== bs_append/8 TODO ==== bs_private_append/6 TODO ==== bs_init_bits/6 TODO ==== bs_get_utf8/5 TODO ==== bs_skip_utf8/4 TODO ==== bs_get_utf16/5 TODO ==== bs_skip_utf16/4 TODO ==== bs_get_utf32/5 TODO ==== bs_skip_utf32/4 TODO ==== bs_utf8_size/3 TODO ==== bs_put_utf8/3 TODO ==== bs_utf16_size/3 TODO ==== bs_put_utf16/3 TODO ==== bs_put_utf32/3 TODO
Argument types
Type | Explanation |
---|---|
t |
A term, e.g. [{foo,bar}] |
I |
An integer e.g. 42 |
x |
A register, e.g. 5 |
y |
A stack slot, e.g. 1 |
c |
A constant (atom,nil,small int) // Pid? |
a |
An atom, e.g. 'foo' |
f |
A label, i.e. a code address |
s |
Either a literal, a register or a stack slot |
d |
Either a register or a stack slot |
r |
A register R0 |
P |
A positive (unsigned) integer literal |
j |
An optional code label |
e |
A reference to an export table entry |
l |
A floating-point register |
List of all BEAM Instructions
Instruction | Arguments | Explanation |
---|---|---|
allocate |
t t |
|
allocate_heap |
t I t |
|
deallocate |
I |
|
init |
y |
|
init2 |
y y |
|
init3 |
y y y |
|
i_trim |
I |
|
test_heap |
I t |
|
allocate_zero |
t t |
|
allocate_heap_zero |
t I t |
|
i_select_val |
r f I |
|
i_select_val |
x f I |
|
i_select_val |
y f I |
|
i_select_val2 |
r f c f c f |
|
i_select_val2 |
x f c f c f |
|
i_select_val2 |
y f c f c f |
|
i_jump_on_val |
rxy f I I |
|
i_jump_on_val_zero |
rxy f I |
|
i_select_tuple_arity |
r f I |
|
i_select_tuple_arity |
x f I |
|
i_select_tuple_arity |
y f I |
|
i_select_tuple_arity2 |
r f A f A f |
|
i_select_tuple_arity2 |
x f A f A f |
|
i_select_tuple_arity2 |
y f A f A f |
|
i_func_info |
I a a I |
|
return |
||
get_list |
rxy rxy rxy |
|
catch |
y f |
|
catch_end |
y |
|
try_end |
y |
|
try_case_end |
s |
|
set_tuple_element |
s d P |
|
i_get_tuple_element |
rxy P rxy |
|
is_number |
f rxy |
|
jump |
f |
|
case_end |
rxy |
|
badmatch |
rxy |
|
if_end |
||
raise |
s s |
|
badarg |
j |
|
system_limit |
j |
|
move_jump |
f ncxy |
|
move_x1 |
c |
|
move_x2 |
c |
|
move2 |
x y x y |
|
move2 |
y x y x |
|
move2 |
x x x x |
|
move |
rxync rxy |
|
recv_mark |
f |
|
i_recv_set |
f |
|
remove_message |
||
timeout |
||
timeout_locked |
||
i_loop_rec |
f r |
|
loop_rec_end |
f |
|
wait |
f |
|
wait_locked |
f |
|
wait_unlocked |
f |
|
i_wait_timeout |
f I |
|
i_wait_timeout |
f s |
|
i_wait_timeout_locked |
f I |
|
i_wait_timeout_locked |
f s |
|
i_wait_error |
||
i_wait_error_locked |
||
send |
||
i_is_eq_exact_immed |
f rxy c |
|
i_is_ne_exact_immed |
f rxy c |
|
i_is_eq_exact_literal |
f rxy c |
|
i_is_ne_exact_literal |
f rxy c |
|
i_is_eq_exact |
f |
|
i_is_ne_exact |
f |
|
i_is_lt |
f |
|
i_is_ge |
f |
|
i_is_eq |
f |
|
i_is_ne |
f |
|
i_put_tuple |
rxy I |
|
put_list |
s s d |
|
i_fetch |
s s |
|
move_return |
xcn r |
|
move_deallocate_return |
xycn r Q |
|
deallocate_return |
Q |
|
test_heap_1_put_list |
I y |
|
is_tuple_of_arity |
f rxy A |
|
is_tuple |
f rxy |
|
test_arity |
f rxy A |
|
extract_next_element |
xy |
|
extract_next_element2 |
xy |
|
extract_next_element3 |
xy |
|
is_integer_allocate |
f rx I I |
|
is_integer |
f rxy |
|
is_list |
f rxy |
|
is_nonempty_list_allocate |
f rx I t |
|
is_nonempty_list_test_heap |
f r I t |
|
is_nonempty_list |
f rxy |
|
is_atom |
f rxy |
|
is_float |
f rxy |
|
is_nil |
f rxy |
|
is_bitstring |
f rxy |
|
is_reference |
f rxy |
|
is_pid |
f rxy |
|
is_port |
f rxy |
|
is_boolean |
f rxy |
|
is_function2 |
f s s |
|
allocate_init |
t I y |
|
i_apply |
||
i_apply_last |
P |
|
i_apply_only |
||
apply |
I |
|
apply_last |
I P |
|
i_apply_fun |
||
i_apply_fun_last |
P |
|
i_apply_fun_only |
||
i_hibernate |
||
call_bif0 |
e |
|
call_bif1 |
e |
|
call_bif2 |
e |
|
call_bif3 |
e |
|
i_get |
s d |
|
self |
rxy |
|
node |
rxy |
|
i_fast_element |
rxy j I d |
|
i_element |
rxy j s d |
|
bif1 |
f b s d |
|
bif1_body |
b s d |
|
i_bif2 |
f b d |
|
i_bif2_body |
b d |
|
i_move_call |
c r f |
|
i_move_call_last |
f P c r |
|
i_move_call_only |
f c r |
|
move_call |
xy r f |
|
move_call_last |
xy r f Q |
|
move_call_only |
x r f |
|
i_call |
f |
|
i_call_last |
f P |
|
i_call_only |
f |
|
i_call_ext |
e |
|
i_call_ext_last |
e P |
|
i_call_ext_only |
e |
|
i_move_call_ext |
c r e |
|
i_move_call_ext_last |
e P c r |
|
i_move_call_ext_only |
e c r |
|
i_call_fun |
I |
|
i_call_fun_last |
I P |
|
i_make_fun |
I t |
|
is_function |
f rxy |
|
i_bs_start_match2 |
rxy f I I d |
|
i_bs_save2 |
rx I |
|
i_bs_restore2 |
rx I |
|
i_bs_match_string |
rx f I I |
|
i_bs_get_integer_small_imm |
rx I f I d |
|
i_bs_get_integer_imm |
rx I I f I d |
|
i_bs_get_integer |
f I I d |
|
i_bs_get_integer_8 |
rx f d |
|
i_bs_get_integer_16 |
rx f d |
|
i_bs_get_integer_32 |
rx f I d |
|
i_bs_get_binary_imm2 |
f rx I I I d |
|
i_bs_get_binary2 |
f rx I s I d |
|
i_bs_get_binary_all2 |
f rx I I d |
|
i_bs_get_binary_all_reuse |
rx f I |
|
i_bs_get_float2 |
f rx I s I d |
|
i_bs_skip_bits2_imm2 |
f rx I |
|
i_bs_skip_bits2 |
f rx rxy I |
|
i_bs_skip_bits_all2 |
f rx I |
|
bs_test_zero_tail2 |
f rx |
|
bs_test_tail_imm2 |
f rx I |
|
bs_test_unit |
f rx I |
|
bs_test_unit8 |
f rx |
|
bs_context_to_binary |
rxy |
|
i_bs_get_utf8 |
rx f d |
|
i_bs_get_utf16 |
rx f I d |
|
i_bs_validate_unicode_retract |
j |
|
i_bs_init_fail |
rxy j I d |
|
i_bs_init_fail_heap |
I j I d |
|
i_bs_init |
I I d |
|
i_bs_init_heap |
I I I d |
|
i_bs_init_heap_bin |
I I d |
|
i_bs_init_heap_bin_heap |
I I I d |
|
i_bs_init_bits_fail |
rxy j I d |
|
i_bs_init_bits_fail_heap |
I j I d |
|
i_bs_init_bits |
I I d |
|
i_bs_init_bits_heap |
I I I d |
|
i_bs_add |
j I d |
|
i_bs_init_writable |
||
i_bs_append |
j I I I d |
|
i_bs_private_append |
j I d |
|
i_new_bs_put_integer |
j s I s |
|
i_new_bs_put_integer_imm |
j I I s |
|
i_bs_utf8_size |
s d |
|
i_bs_utf16_size |
s d |
|
i_bs_put_utf8 |
j s |
|
i_bs_put_utf16 |
j I s |
|
i_bs_validate_unicode |
j s |
|
i_new_bs_put_float |
j s I s |
|
i_new_bs_put_float_imm |
j I I s |
|
i_new_bs_put_binary |
j s I s |
|
i_new_bs_put_binary_imm |
j I s |
|
i_new_bs_put_binary_all |
j s I |
|
bs_put_string |
I I |
|
fmove |
qdl ld |
|
fconv |
d l |
|
i_fadd |
l l l |
|
i_fsub |
l l l |
|
i_fmul |
l l l |
|
i_fdiv |
l l l |
|
i_fnegate |
l l l |
|
i_fcheckerror |
||
fclearerror |
||
i_increment |
rxy I I d |
|
i_plus |
j I d |
|
i_minus |
j I d |
|
i_times |
j I d |
|
i_m_div |
j I d |
|
i_int_div |
j I d |
|
i_rem |
j I d |
|
i_bsl |
j I d |
|
i_bsr |
j I d |
|
i_band |
j I d |
|
i_bor |
j I d |
|
i_bxor |
j I d |
|
i_int_bnot |
j s I d |
|
i_gc_bif1 |
j I s I d |
|
i_gc_bif2 |
j I I d |
|
i_gc_bif3 |
j I s I d |
|
int_code_end |
||
label |
L |
|
line |
I |