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).
Note
|
Function info instruction points to an Export record (defined in erts/emulator/beam/export.h ) and located somewhere else in memory. The few dedicated words of memory inside that record are used by the tracing mechanism to place a special trace instruction which will trigger for each entry/return from the function by all processes.
|
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. Current code address before the call is saved into CP.
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. The CP is not updated with the return address.
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. The CP is not updated with the return address.
Does an external call to the function of arity Arity
given by Destination. Destination in assembly is usually written as {extfunc, Module, Function, Arity}
, this is then added to imports section of the module. First count down the reductions and if needed do a context switch. CP will be updated with the return address.
Does a tail recursive external call to the function of arity Arity
given by Destination. Destination in assembly is usually written as {extfunc, Module, Function, Arity}
. First count down the reductions and if needed do a context switch. The CP is not updated with the return address.
Deallocate Deallocate
words of stack, then do a tail recursive external call to the function of arity Arity
given by Destination. Destination in assembly is usually written as {extfunc, Module, Function, Arity}
. First count down the reductions and if needed do a context switch. The CP is not updated with the return address.
Call the bif Bif
with the given arguments, and store the result in Reg
. If the bif fails, jump to Lbl
. Zero arity bif cannot fail and thus bif0
doesn’t take a fail label.
Tip
|
Bif called by these instructions may not allocate on the heap nor trigger a garbage collection. Otherwise see: gc_bif .
|
Call the bif Bif
with the given arguments, and store the result in Reg
. If the bif fails, jump to Lbl
. Arguments will be stored in x(Live)
, x(Live+1)
and x(Live+2)
.
Tip
|
Because this instruction has argument Live , it gives us enough information to be able to trigger the garbage collection.
|
The instruction call_fun
assumes that the arguments are placed in the first Arity
argument registers and that the fun (the pointer to the closure) is placed in the register following the last argument x[Arity+1]
.
That is, for a zero arity call, the closure is placed in x[0]
. For a arity 1 call x[0]
contains the argument and x[1]
contains the closure and so on.
Note
|
Raises badarity if the arity doesn’t match the function object. Raises badfun if a non-function is passed.
|
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 during allocation we run out of memory, call the GC and then first Live
x registers will form a part of the root set. E.g. if Live
is 2 then GC will save registers X0 and X1, rest are unused and will be freed.
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 during allocation we run out of memory, call the GC with Live
amount of X registers to preserve.
Note
|
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 during allocation we run out of memory, call the GC with Live
amount of X registers to preserve.
The deallocate
instruction is the opposite of the allocate
. It restores the CP (continuation pointer) and deallocates N+1
stack words.
The return instructions jumps to the address in the continuation pointer (CP). The value of CP is set to 0
in C.
Pops the CP into a temporary variable, frees N
words of stack, and places the CP back onto the top of the stack. (The argument Remaining
is to the best of my knowledge unused.)
Before After | ??? | | ??? | | xxx | E -> | CP | | xxx | | ... | E -> | CP | | ... | | | | ... | ... ... HTOP -> | | HTOP -> | | | xxx | | xxx |
Moves the value of the source Source
(this can be a literal or a register) to the destination register Destination
.
This is a deconstruct operation for a list cell. Get the head and tail (or car and cdr) parts of a list (a cons cell), specified by Source
and place them into the registers Head
and Tail
.
This is an array indexed read operation. Get element with position Element
from the Source
tuple and place it into the Destination
register.
Constructs a new list (cons) cell on the heap (2 words) and places its address into the Destination
register. First element of list cell is set to the value of Head
, second element is set to the value of Tail
.
Constructs an empty tuple on the heap (Size+1
words) and places its address into the Destination
register. No elements are set at this moment. Put_tuple
instruction is always followed by multiple put
instructions which destructively set its elements one by one.
Places destructively a Value
into the next element of a tuple, which was created by a preceding put_tuple
instruction. Write address is maintained and incremented internally by the VM. Multiple put
instructions are used to set contents for any new tuple.
Creates a function object defined by an index in the Lambda table of the module. A lambda table defines the entry point (a label or export entry), arity and how many frozen variables to take. Frozen variable values are copied from the current execution context (X registers) and stored into the function object.
Argument types
Type | Explanation |
---|---|
a |
An immediate atom value, e.g. 'foo' |
c |
An immediate constant value (atom, nil, small int) // Pid? |
d |
Either a register or a stack slot |
e |
A reference to an export table entry |
f |
A label, i.e. a code address |
I |
An integer e.g. |
j |
An optional code label |
l |
A floating-point register |
P |
A positive (unsigned) integer literal |
r |
A register R0 ( |
s |
Either a literal, a register or a stack slot |
t |
A term, e.g. |
x |
A register, e.g. |
y |
A stack slot, e.g. |
List of all BEAM Instructions
Instruction | Arguments | Explanation |
---|---|---|
allocate |
t t |
Allocate some words on stack |
allocate_heap |
t I t |
Allocate some words on the heap |
allocate_heap_zero |
t I t |
Allocate some heap and set the words to NIL |
allocate_init |
t I y |
|
allocate_zero |
t t |
Allocate some stack and set the words to NIL |
apply |
I |
Apply function object (in x[Arity]) with args (in x[0..Arity-1]) |
apply_last |
I P |
Same as |
badarg |
j |
Create a |
badmatch |
rxy |
Create a |
bif1 |
f b s d |
Calls a bif with 1 argument, on fail jumps to |
bif1_body |
b s d |
|
bs_context_to_binary |
rxy |
|
bs_put_string |
I I |
|
bs_test_tail_imm2 |
f rx I |
|
bs_test_unit |
f rx I |
|
bs_test_unit8 |
f rx |
|
bs_test_zero_tail2 |
f rx |
|
call_bif0 |
e |
|
call_bif1 |
e |
|
call_bif2 |
e |
|
call_bif3 |
e |
|
case_end |
rxy |
Create a |
catch |
y f |
|
catch_end |
y |
|
deallocate |
I |
Free some words from stack and pop CP |
deallocate_return |
Q |
|
extract_next_element |
xy |
|
extract_next_element2 |
xy |
|
extract_next_element3 |
xy |
|
fclearerror |
||
fconv |
d l |
|
fmove |
qdl ld |
|
get_list |
rxy rxy rxy |
Deconstruct a list cell into the head and the tail |
i_apply |
||
i_apply_fun |
||
i_apply_fun_last |
P |
|
i_apply_fun_only |
||
i_apply_last |
P |
|
i_apply_only |
||
i_band |
j I d |
|
i_bif2 |
f b d |
|
i_bif2_body |
b d |
|
i_bor |
j I d |
|
i_bs_add |
j I d |
|
i_bs_append |
j 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_binary_imm2 |
f rx I I I d |
|
i_bs_get_float2 |
f rx I s I d |
|
i_bs_get_integer |
f I I d |
|
i_bs_get_integer_16 |
rx f d |
|
i_bs_get_integer_32 |
rx f I d |
|
i_bs_get_integer_8 |
rx f d |
|
i_bs_get_integer_imm |
rx I I f I d |
|
i_bs_get_integer_small_imm |
rx I f I d |
|
i_bs_get_utf16 |
rx f I d |
|
i_bs_get_utf8 |
rx f d |
|
i_bs_init |
I I d |
|
i_bs_init_bits |
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_heap |
I I I d |
|
i_bs_init_fail |
rxy j I d |
|
i_bs_init_fail_heap |
I j 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_writable |
||
i_bs_match_string |
rx f I I |
|
i_bs_private_append |
j I d |
|
i_bs_put_utf16 |
j I s |
|
i_bs_put_utf8 |
j s |
|
i_bs_restore2 |
rx I |
|
i_bs_save2 |
rx I |
|
i_bs_skip_bits2 |
f rx rxy I |
|
i_bs_skip_bits2_imm2 |
f rx I |
|
i_bs_skip_bits_all2 |
f rx I |
|
i_bs_start_match2 |
rxy f I I d |
|
i_bs_utf16_size |
s d |
|
i_bs_utf8_size |
s d |
|
i_bs_validate_unicode |
j s |
|
i_bs_validate_unicode_retract |
j |
|
i_bsl |
j I d |
|
i_bsr |
j I d |
|
i_bxor |
j I d |
|
i_call |
f |
|
i_call_ext |
e |
|
i_call_ext_last |
e P |
|
i_call_ext_only |
e |
|
i_call_fun |
I |
|
i_call_fun_last |
I P |
|
i_call_last |
f P |
|
i_call_only |
f |
|
i_element |
rxy j s d |
|
i_fadd |
l l l |
|
i_fast_element |
rxy j I d |
|
i_fcheckerror |
||
i_fdiv |
l l l |
|
i_fetch |
s s |
|
i_fmul |
l l l |
|
i_fnegate |
l l l |
|
i_fsub |
l l l |
|
i_func_info |
I a a I |
Create a |
i_gc_bif1 |
j I s I d |
|
i_gc_bif2 |
j I I d |
|
i_gc_bif3 |
j I s I d |
|
i_get |
s d |
|
i_get_tuple_element |
rxy P rxy |
|
i_hibernate |
||
i_increment |
rxy I I d |
|
i_int_bnot |
j s I d |
|
i_int_div |
j I d |
|
i_is_eq |
f |
|
i_is_eq_exact |
f |
|
i_is_eq_exact_immed |
f rxy c |
|
i_is_eq_exact_literal |
f rxy c |
|
i_is_ge |
f |
|
i_is_lt |
f |
|
i_is_ne |
f |
|
i_is_ne_exact |
f |
|
i_is_ne_exact_immed |
f rxy c |
|
i_is_ne_exact_literal |
f rxy c |
|
i_jump_on_val |
rxy f I I |
|
i_jump_on_val_zero |
rxy f I |
|
i_loop_rec |
f r |
|
i_m_div |
j I d |
|
i_make_fun |
I t |
|
i_minus |
j I d |
|
i_move_call |
c r f |
|
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_move_call_last |
f P c r |
|
i_move_call_only |
f c r |
|
i_new_bs_put_binary |
j s I s |
|
i_new_bs_put_binary_all |
j s I |
|
i_new_bs_put_binary_imm |
j I s |
|
i_new_bs_put_float |
j s I s |
|
i_new_bs_put_float_imm |
j I I s |
|
i_new_bs_put_integer |
j s I s |
|
i_new_bs_put_integer_imm |
j I I s |
|
i_plus |
j I d |
|
i_put_tuple |
rxy I |
|
i_recv_set |
f |
|
i_rem |
j I d |
|
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_select_val |
r f I |
Compare value to a list of pairs |
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_times |
j I d |
|
i_trim |
I |
|
i_wait_error |
||
i_wait_error_locked |
||
i_wait_timeout |
f I |
|
i_wait_timeout |
f s |
|
i_wait_timeout_locked |
f I |
|
i_wait_timeout_locked |
f s |
|
if_end |
Create an |
|
init |
y |
Set some words on stack to NIL |
init2 |
y y |
|
init3 |
y y y |
|
int_code_end |
||
is_atom |
f rxy |
|
is_bitstring |
f rxy |
|
is_boolean |
f rxy |
|
is_float |
f rxy |
|
is_function |
f rxy |
|
is_function2 |
f s s |
|
is_integer |
f rxy |
|
is_integer_allocate |
f rx I I |
|
is_list |
f rxy |
|
is_nil |
f rxy |
|
is_nonempty_list |
f rxy |
|
is_nonempty_list_allocate |
f rx I t |
|
is_nonempty_list_test_heap |
f r I t |
|
is_number |
f rxy |
Check if the value is an integer or a float, else jump to |
is_pid |
f rxy |
|
is_port |
f rxy |
|
is_reference |
f rxy |
|
is_tuple |
f rxy |
|
is_tuple_of_arity |
f rxy A |
|
jump |
f |
Jump to |
label |
L |
Marks a location in code, removed at the load time |
line |
I |
Marks a location in source file, removed at the load time |
loop_rec_end |
f |
|
move |
rxync rxy |
Moves a value or a register into another register |
move2 |
x x x x |
|
move2 |
x y x y |
|
move2 |
y x y x |
|
move_call |
xy r f |
|
move_call_last |
xy r f Q |
|
move_call_only |
x r f |
|
move_deallocate_return |
xycn r Q |
|
move_jump |
f ncxy |
|
move_return |
xcn r |
|
move_x1 |
c |
|
move_x2 |
c |
|
node |
rxy |
Get |
put_list |
s s d |
Construct a list cell from a head and a tail |
raise |
s s |
Raise an exception of given type |
recv_mark |
f |
|
remove_message |
||
return |
Jump to the address in CP, set CP to 0 |
|
self |
rxy |
Set |
send |
||
set_tuple_element |
s d P |
Destructively update a tuple element by index |
system_limit |
j |
|
test_arity |
f rxy A |
|
test_heap |
I t |
Check the heap space availability |
test_heap_1_put_list |
I y |
|
timeout |
||
timeout_locked |
||
try_case_end |
s |
|
try_end |
y |
|
wait |
f |
|
wait_locked |
f |
|
wait_unlocked |
f |