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
...               ...
							Lennart Fridén
codecoupled.org | @DevLCSC | github.com/DevL
ElixirConf 2015, Austin, TX