Lennart Fridén
codecoupled.org | @DevLCSC | github.com/DevL
ElixirConf 2015, Austin, TX
defmodule Virtually do
def instructional do
"Hello ElixirConf"
end
end
$ hexdump -C Elixir.Virtually.beam
00000000 46 4f 52 31 00 00 04 dc 42 45 41 4d 45 78 44 63 FOR1....BEAMExDc
00000090 41 74 6f 6d Atom
000000a0 00 00 00 60 00 00 00 08 10 45 6c 69 78 69 72 2e ...`.....Elixir.
000000b0 56 69 72 74 75 61 6c 6c 79 08 5f 5f 69 6e 66 6f Virtually.__info
000000c0 5f 5f 09 66 75 6e 63 74 69 6f 6e 73 06 6d 61 63 __.functions.mac
000000d0 72 6f 73 06 65 72 6c 61 6e 67 0f 67 65 74 5f 6d ros.erlang.get_m
000000e0 6f 64 75 6c 65 5f 69 6e 66 6f 0d 69 6e 73 74 72 odule_info.instr
000000f0 75 63 74 69 6f 6e 61 6c 0b 6d 6f 64 75 6c 65 5f uctional.module_
00000100 69 6e 66 6f info
00000100 43 6f 64 65 00 00 00 7b 00 00 00 10 Code...{....
00000110 00 00 00 00 00 00 00 99 00 00 00 0c 00 00 00 04 ................
00000120 01 10 99 00 02 12 22 10 01 20 30 55 03 3b 03 55 ......".. 0U.;.U
00000130 17 40 32 35 42 45 01 30 40 47 00 03 13 01 40 40 .@25BE.0@G....@@
00000140 02 03 13 01 50 40 03 13 40 12 03 99 00 4e 20 00 ....P@..@....N .
00000150 01 60 99 10 02 12 72 00 01 70 40 47 10 03 13 01 .`....r..p@G....
00000160 80 99 00 02 12 82 00 01 90 40 12 03 99 00 4e 10 .........@....N.
00000170 10 01 a0 99 00 02 12 82 10 01 b0 40 03 13 40 12 ...........@..@.
00000180 03 99 00 4e 20 00 03 00 ...N ...
$ ERL_COMPILER_OPTIONS="'S'" elixirc virtually.ex
{move,{literal,{instructional,[{line,2}],nil}},{x,3}}.
{move,{atom,def},{x,1}}.
{move,{literal,[{do,<<"Hello ElixirConf">>}]},{x,4}}.
{move,{integer,2},{x,0}}.
{line,[{location,"virtually.ex",2}]}.
{call_ext,6,{extfunc,elixir_def,store_definition,6}}.
{move,{literal,<<"000000001574CCC0: i_func_info_IaaI 0 'Elixir.Virtually' instructional 0\n000000001574CCE8: move_return_cr <<\"Hello ElixirConf\">> x(0)\n">>},
{x,0}}.
{deallocate,0}.
return.
https://github.com/erlang/otp/blob/master/lib/compiler/src/genop.tab
1: label/1
2: func_info/3
3: int_code_end/0
4: call/2
5: call_last/3
6: call_only/2
...
64: move/2
65: get_list/3
66: get_tuple_element/3
67: set_tuple_element/3
...
154: put_map_assoc/5
155: put_map_exact/5
156: is_map/2
157: has_map_fields/3
158: get_map_elements/3
Generic | Transformed |
---|---|
(27) -m_plus/4 | i_increment |
(57) is_tuple/2 (58) test_arity/3 |
is_tuple_of_arity |
iex(1)> :erts_debug.df Virtually
(memory address): (instruction)_(operand types) arg0 arg1 ...
000000001574CCC0: i_func_info_IaaI 0 'Elixir.Virtually' instructional 0
000000001574CCE8: move_return_cr <<"Hello ElixirConf">> x(0)
A tiny sliver of memory built into a CPU.
Tiny. But very, very fast to access.
BEAM, Parrot (Perl 6 et al), Dalvik (Android), Lua
public class StackMachine {
public int doubleSumOf(int a, int b) {
return 2 * (a + b);
}
}
$ javac StackMachine.java
$ javap -c StackMachine.class
Code:
0: iconst_2 // stack = [2]
1: iload_1 // stack = [a, 2]
2: iload_2 // stack = [b, a, 2]
3: iadd // stack = [(a + b), 2]
4: imul // stack = [2 * (a + b)]
5: ireturn // stack = []
Trades performance for ease of implementation
Yunhe Shi
https://www.scss.tcd.ie/publications/tech-reports/reports.07/TCD-CS-2007-49.pdf
def decode(instruction, x, y) do
if instruction == :add do
x + y
else if instruction == :subtract do
x - y
else if instruction == :divide do
x / y
else
raise "Unknown instruction"
end
end
end
end
def decode(:add, x, y), do: x + y
def decode(:subtract, x, y), do: x - y
def decode(:divide, x, y), do: x / y
def impractical(x) when is_integer(x), do: x + 1
def impractical(x) when is_float(x), do: x / 2
def impractical(_), do: :batman
000000001574CBC0: i_func_info_IaaI 0 'Elixir.Virtually' impractical 1
000000001574CBE8: is_integer_fr f(000000001574CC20) x(0)
000000001574CBF8: i_increment_rIId x(0) 1 1 x(0)
000000001574CC18: return
000000001574CC20: is_float_fr f(000000001574CCB0) x(0)
000000001574CC30: test_heap_It 2 1
000000001574CC40: fmove_dl x(0) fr(0)
000000001574CC58: fmove_ql 2.000000e+00 fr(1)
000000001574CC70: i_fdiv_lll fr(0) fr(1) fr(0)
000000001574CC90: fmove_ld fr(0) x(0)
000000001574CCA8: return
000000001574CCB0: move_return_cr batman x(0)
000000001574CBC0: i_func_info_IaaI 0 'Elixir.Virtually' impractical 1
000000001574CBE8: is_integer_fr f(000000001574CC20) x(0)
000000001574CBF8: i_increment_rIId x(0) 1 1 x(0)
000000001574CC18: return
000000001574CC20: is_float_fr f(000000001574CCB0) x(0)
000000001574CC30: test_heap_It 2 1
000000001574CC40: fmove_dl x(0) fr(0)
000000001574CC58: fmove_ql 2.000000e+00 fr(1)
000000001574CC70: i_fdiv_lll fr(0) fr(1) fr(0)
000000001574CC90: fmove_ld fr(0) x(0)
000000001574CCA8: return
000000001574CCB0: move_return_cr batman x(0)
Register | Purpose | In code |
---|---|---|
R0 - R255 | general purpose | x(n) |
FR0 - FR15 | floating-point operations | fr(n) |
tmpA, tmpB | temporary | not visible |
stack slots | local variables | y(n) |
Register | Purpose |
---|---|
I | native integer type |
N | floating-point numbers |
S | strings |
P | PMC (Polymorphic Container) |
def incommunicado do
receive do
{:ping, sender} -> send sender, :pong
_ -> :ignore
end
incommunicado
end
000000001A9C1468: i_func_info_IaaI 0 'Elixir.Virtually' incommunicado 0
000000001A9C1490: allocate_tt 0 0
000000001A9C14A0: i_loop_rec_fr f(000000001A9C1558) x(0)
000000001A9C14B0: is_tuple_of_arity_frA f(000000001A9C1540) x(0) 2
000000001A9C14C8: extract_next_element2_x x(1)
000000001A9C14D8: i_is_eq_exact_immed_fxc f(000000001A9C1540) x(1) ping
000000001A9C14F8: remove_message
000000001A9C1500: move_x1_c pong
000000001A9C1510: move_xr x(2) x(0)
000000001A9C1520: call_bif_e erlang:send/2
000000001A9C1530: i_is_lt_spec_frr f(000000001A9C1568) x(0) x(0)
000000001A9C1540: remove_message
000000001A9C1548: i_is_lt_spec_frr f(000000001A9C1568) x(0) x(0)
000000001A9C1558: wait_f f(000000001A9C14A0)
000000001A9C1568: i_call_last_fP 'Elixir.Virtually':incommunicado/0 0
000000001A9C1468: i_func_info_IaaI 0 'Elixir.Virtually' incommunicado 0
000000001A9C1490: allocate_tt 0 0
000000001A9C14A0: i_loop_rec_fr f(000000001A9C1558) x(0)
000000001A9C14B0: is_tuple_of_arity_frA f(000000001A9C1540) x(0) 2
000000001A9C14C8: extract_next_element2_x x(1)
000000001A9C14D8: i_is_eq_exact_immed_fxc f(000000001A9C1540) x(1) ping
000000001A9C14F8: remove_message
000000001A9C1500: move_x1_c pong
000000001A9C1510: move_xr x(2) x(0)
000000001A9C1520: call_bif_e erlang:send/2
000000001A9C1530: i_is_lt_spec_frr f(000000001A9C1568) x(0) x(0)
000000001A9C1540: remove_message
000000001A9C1548: i_is_lt_spec_frr f(000000001A9C1568) x(0) x(0)
000000001A9C1558: wait_f f(000000001A9C14A0)
000000001A9C1568: i_call_last_fP 'Elixir.Virtually':incommunicado/0 0
VM | Instructions |
---|---|
BEAM |
message handling (send, remove message) type checks (is_integer, is_tuple) |
Parrot |
trigonemetrics (sin, cos, atan) (Dynamically loaded if requested) |
JVM | multianewarray (allocate a multi-dimensional array) |
def intractable({x, y}) do
"The afterparty will be at #{x}, #{y}."
end
000000001574CE10: i_func_info_IaaI 0 'Elixir.Virtually' intractable 1
000000001574CE38: is_tuple_of_arity_frA f(000000001574CE10) x(0) 2
000000001574CE50: allocate_zero_tt 2 1
000000001574CE60: i_get_tuple_element_rPx x(0) 0 x(1)
000000001574CE78: extract_next_element_y y(1)
000000001574CE88: is_binary_fx f(000000001574CEB8) x(1)
000000001574CEA0: move_jump_fx f(000000001574CED8) x(1)
000000001574CEB8: move_xr x(1) x(0)
000000001574CEC8: i_call_ext_e 'Elixir.String.Chars':to_string/1
000000001574CED8: move_ry x(0) y(0)
000000001574CEE8: is_binary_fy f(000000001574CF18) y(1)
000000001574CF00: move_jump_fy f(000000001574CF48) y(1)
000000001574CF18: move_yr y(1) x(0)
000000001574CF28: init_y y(1)
000000001574CF38: i_call_ext_e 'Elixir.String.Chars':to_string/1
000000001574CF48: move_x1_c 0
000000001574CF58: i_gc_bif1_jIsId j(0000000000000000) 348929271 x(0) 2 x(2)
000000001574CF88: i_fetch_xx x(1) x(2)
000000001574CF98: i_bs_add_jId j(0000000000000000) 1 x(1)
000000001574CFB8: i_gc_bif1_jIsId j(0000000000000000) 348929271 y(0) 2 x(2)
000000001574CFE8: i_fetch_xx x(1) x(2)
000000001574CFF8: i_bs_add_jId j(0000000000000000) 1 x(1)
000000001574D018: i_fetch_xc x(1) 29
000000001574D030: i_bs_add_jId j(0000000000000000) 1 x(1)
000000001574D050: i_bs_init_fail_xjId x(1) j(0000000000000000) 1 x(1)
000000001574D078: bs_put_string_II 26 359977936
000000001574D090: i_new_bs_put_binary_all_jsI j(0000000000000000) y(0) 8
000000001574D0B0: bs_put_string_II 2 359977962
000000001574D0C8: i_new_bs_put_binary_all_jsI j(0000000000000000) x(0) 8
000000001574D0E8: bs_put_string_II 1 359977964
000000001574D100: move_deallocate_return_xrQ x(1) x(0) 2
000000001574CE10:
000000001574CE38:
000000001574CE50:
000000001574CE60:
000000001574CE78:
000000001574CE88:
000000001574CEA0:
000000001574CEB8:
000000001574CEC8: i_call_ext_e 'Elixir.String.Chars':to_string/1
000000001574CED8:
000000001574CEE8:
000000001574CF00:
000000001574CF18:
000000001574CF28:
000000001574CF38: i_call_ext_e 'Elixir.String.Chars':to_string/1
000000001574CF48:
000000001574CF58: i_gc_bif1_jIsId j(0000000000000000) 348929271 x(0) 2 x(2)
000000001574CF88:
000000001574CF98:
000000001574CFB8: i_gc_bif1_jIsId j(0000000000000000) 348929271 y(0) 2 x(2)
000000001574CFE8:
000000001574CFF8:
000000001574D018:
000000001574D030:
000000001574D050:
000000001574D078:
000000001574D090:
000000001574D0B0:
000000001574D0C8:
000000001574D0E8:
000000001574D100:
0000000014FC0CA8: i_func_info_IaaI 0 'Elixir.Enum' each 2
0000000014FC0CD0: is_list_fr f(0000000014FC0D18) x(0)
0000000014FC0CE0: allocate_tt 0 2
0000000014FC0CF0: i_call_f 'Elixir.Enum':'-each/2-lists^foreach/1-0-'/2
0000000014FC0D00: move_deallocate_return_crQ ok x(0) 0
0000000014FC0D18: allocate_tt 1 2
0000000014FC0D28: move_ry x(0) y(0)
0000000014FC0D38: move_xr x(1) x(0)
0000000014FC0D48: i_make_fun_It 344639352 1
0000000014FC0D60: move_x1_c nil
0000000014FC0D70: move_rx x(0) x(2)
0000000014FC0D80: move_yr y(0) x(0)
0000000014FC0D90: i_trim_I 1
0000000014FC0DA0: i_call_f 'Elixir.Enum':reduce/3
0000000014FC0DB0: move_deallocate_return_crQ ok x(0) 0
00000000161A5970: i_func_info_IaaI 0 erts_debug size 3
00000000161A5998: is_nonempty_list_allocate_frIt f(00000000161A5B00) x(0) 4 3
00000000161A59B0: get_list_ryy x(0) y(3) y(2)
00000000161A59C0: move2_xyxy x(2) y(0) x(1) y(1)
00000000161A59D0: i_call_f erts_debug:remember_term/2
... ...
Yes, disassemble the function that disassemble functions and study it.
Lennart Fridén
codecoupled.org | @DevLCSC | github.com/DevL
ElixirConf 2015, Austin, TX