Skip to content

Latest commit

 

History

History
753 lines (539 loc) · 24.8 KB

ap-beam_instructions.asciidoc

File metadata and controls

753 lines (539 loc) · 24.8 KB

BEAM Instructions

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).

Functions and Labels

label Lbl

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.

func_info Module Function Arity

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.

Test instructions

Type tests

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.

Comparisons

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 =/=.

Function Calls

In this chapter we will summarize what the different call instructions does. For a thorough description of how function calls work see [CH-Calls].

call Arity Label

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.

call_last Arity Label

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.

call_only Arity Label Deallocate

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.

call_ext Arity Destination

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.

call_ext_only Arity Destination

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.

call_ext_last Arity Destination Deallocate

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.

bif0 Bif Reg, bif[1,2] Lbl Bif [Arg,…​] Reg

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.

gc_bif[1-3] Lbl Live Bif [Arg, …​] Reg

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.

call_fun Arity

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.

apply/1

TODO

apply_last/2

TODO

Stack (and Heap) Management

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.

allocate StackNeed Live

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.

Example 1. Allocate 1 0
       Before           After
         | xxx |            | xxx |
    E -> | xxx |            | xxx |
         |     |            | ??? | caller save slot
           ...         E -> | CP  |
           ...                ...
 HTOP -> |     |    HTOP -> |     |
         | xxx |            | xxx |

allocate_heap StackNeed HeapNeed Live

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.

allocate_zero StackNeed Live

This instruction works the same way as allocate, but it also clears out the allocated stack slots with NIL.

Example 2. allocate_zero 1 0
       Before           After
         | xxx |            | xxx |
    E -> | xxx |            | xxx |
         |     |            | NIL | caller save slot
           ...         E -> | CP  |
           ...                ...
 HTOP -> |     |    HTOP -> |     |
         | xxx |            | xxx |

allocate_heap_zero StackNeed HeapNeed Live

The allocate_heap_zero instruction works as the allocate_heap instruction, but it also clears out the allocated stack slots with NIL.

test_heap HeapNeed Live

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.

init N

The init instruction clears N stack words above the CP pointer by writing NIL to them.

deallocate N

The deallocate instruction is the opposite of the allocate. It restores the CP (continuation pointer) and deallocates N+1 stack words.

return

The return instructions jumps to the address in the continuation pointer (CP). The value of CP is set to 0 in C.

trim N Remaining

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.)

Example 3. Trim 2
       Before           After
         | ??? |            | ??? |
         | xxx |       E -> | CP  |
         | xxx |            | ... |
    E -> | CP  |            | ... |
         |     |            | ... |
           ...                ...
 HTOP -> |     |    HTOP -> |     |
         | xxx |            | xxx |

Moving, extracting, modifying data

move Source Destination

Moves the value of the source Source (this can be a literal or a register) to the destination register Destination.

get_list Source Head Tail

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.

get_tuple_element Source Element Destination

This is an array indexed read operation. Get element with position Element from the Source tuple and place it into the Destination register.

set_tuple_element NewElement Tuple Position

This is a destructive array indexed update operation. Update the element of the Tuple at Position with the new NewElement.

Building terms.

put_list Head Tail Destination

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.

put_tuple Size Destination

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.

put Value

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.

make_fun2 LambdaIndex

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.

Binary Syntax

bs_put_integer/5

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

Floating Point Arithmetic

fclearerror/0

TODO

fcheckerror/1

TODO

fmove/2

TODO

fconv/2

TODO

fadd/4

TODO

fsub/4

TODO

fmul/4

TODO

fdiv/4

TODO

fnegate/3

TODO

Pattern Matching

select_val

TODO

select_arity_val

TODO

jump

TODO

Exception handling

catch/2

TODO

catch_end/1

TODO

badmatch/1

TODO

if_end/0

TODO

case_end/1

TODO

Meta instructions

on_load

TODO

Specific Instructions

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. 42

j

An optional code label

l

A floating-point register

P

A positive (unsigned) integer literal

r

A register R0 (x[0])

s

Either a literal, a register or a stack slot

t

A term, e.g. [{foo, bar}]

x

A register, e.g. 5 for {x, 5}

y

A stack slot, e.g. 1 for {y, 1}

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 apply but does not save the CP and deallocates P words

badarg

j

Create a badarg error

badmatch

rxy

Create a badmatch error

bif1

f b s d

Calls a bif with 1 argument, on fail jumps to f

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 case_clause error

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 function_clause error

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 {Value, Label} and jump when a match is found, otherwise jump to f

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 if_clause error

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 f

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 f

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 rxy to the atom, current node name

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 rxy to the pid of the current process

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