Erlang源代码编译为beam文件,代码要经过一系列的过程(见下面的简图),Core Erlang之前已经简单介绍过了Core Erlang,代码转换为Core Erlang,就容易拨开一些语法糖的真面目了.下一阶段就是将Core Erlang转换为opcode,使用c(m,'S')生成的是.S文件可以看到反编译的代码.编译器最终输出的是Virtual Beam Code 但这还不是最终VM执行的代码,在erts\emulator\beam\beam_load.c执行的过程中会完成指令优化,优化之后的代码可以通过erts_debug:df(m)生成.dis文件查看,换句话说,这个方法执行成功必须要要求模块已经加载.
下面的代码里面,模块t还没有编译,所以第一次执行erts_debug:df(t)返回值是{undef,t}
1 2 3 4 5 6 7 8 9 10 11 12 13 | Eshell V6.0 (abort with ^G) 1> c(t, 'S' ). ** Warning: No object file created - nothing loaded ** ok 2> erts_debug:df(t). {undef,t} 3> c(t). {ok,t} 4> erts_debug:df(t). ok 5> q(). ok 6> |
之前有两次使用过反汇编的代码:
[Erlang 0029] Erlang Inline编译 [链接] 里面我们使用过反编译的代码观察inline机制.
[Erlang 0100] make_ref 与 Selective Receive [链接]通过反汇编代码看到selective receive的优化处理.
除了Erlang源代码几乎没有什么资料可供参考.有两个方法可以继续跟踪下去,一是通过erts_debug:instructions()输出所有命令,再就是编写erl代码对照.S代码去推测意义.霸爷有一篇<实验Erlang语法对应的opcode 让你对erlang理解更深>就是这样做的,下面把我比较关注的一些地方做下测试.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | Eshell V6.0 (abort with ^G) 1> rp(erts_debug:instructions()). [ "allocate_tt" , "allocate_heap_tIt" , "allocate_heap_zero_tIt" , "allocate_init_tIy" , "allocate_zero_tt" , "apply_I" , "apply_bif" , "apply_last_IP" , "badarg_j" , "badmatch_r" , "badmatch_x" , "badmatch_y" , "bif1_fbsd" , "bif1_body_bsd" , "bs_context_to_binary_r" , "bs_context_to_binary_x" , "bs_context_to_binary_y" , "bs_init_writable" , "bs_put_string_II" , "bs_test_tail_imm2_frI" , "bs_test_tail_imm2_fxI" , "bs_test_unit_frI" , "bs_test_unit_fxI" , "bs_test_unit8_fr" , "bs_test_unit8_fx" , "bs_test_zero_tail2_fr" , "bs_test_zero_tail2_fx" , "call_bif_e" , "call_error_handler" , "call_nif" , "case_end_r" , "case_end_x" , "case_end_y" , "catch_yf" , "catch_end_y" , "continue_exit" , "deallocate_I" , "deallocate_return_Q" , "error_action_code" , "extract_next_element_x" , "extract_next_element_y" , "extract_next_element2_x" , "extract_next_element2_y" , "extract_next_element3_x" , "extract_next_element3_y" , "fclearerror" , "fconv_dl" , "fmove_ql" , "fmove_ld" , "fmove_dl" , "get_list_rrx" , "get_list_rry" , "get_list_rxr" , "get_list_rxx" , "get_list_rxy" , "get_list_ryr" , "get_list_ryx" , "get_list_ryy" , "get_list_xrx" , "get_list_xry" , "get_list_xxr" , "get_list_xxx" , "get_list_xxy" , "get_list_xyr" , "get_list_xyx" , "get_list_xyy" , "get_list_yrx" , "get_list_yry" , "get_list_yxr" , "get_list_yxx" , "get_list_yxy" , "get_list_yyr" , "get_list_yyx" , "get_list_yyy" , "hipe_call_count" , "hipe_trap_call" , "hipe_trap_call_closure" , "hipe_trap_resume" , "hipe_trap_return" , "hipe_trap_throw" , "i_apply" , "i_apply_fun" , "i_apply_fun_last_P" , "i_apply_fun_only" , "i_apply_last_P" , "i_apply_only" , "i_band_jId" , "i_bif2_fbd" , "i_bif2_body_bd" , "i_bor_jId" , "i_bs_add_jId" , "i_bs_append_jIIId" , "i_bs_get_binary2_frIsId" , "i_bs_get_binary2_fxIsId" , "i_bs_get_binary_all2_frIId" , "i_bs_get_binary_all2_fxIId" , "i_bs_get_binary_all_reuse_rfI" , "i_bs_get_binary_all_reuse_xfI" , "i_bs_get_binary_imm2_frIIId" , "i_bs_get_binary_imm2_fxIIId" , "i_bs_get_float2_frIsId" , "i_bs_get_float2_fxIsId" , "i_bs_get_integer_fIId" , "i_bs_get_integer_16_rfd" , "i_bs_get_integer_16_xfd" , "i_bs_get_integer_32_rfId" , "i_bs_get_integer_32_xfId" , "i_bs_get_integer_8_rfd" , "i_bs_get_integer_8_xfd" , "i_bs_get_integer_imm_rIIfId" , "i_bs_get_integer_imm_xIIfId" , "i_bs_get_integer_small_imm_rIfId" , "i_bs_get_integer_small_imm_xIfId" , "i_bs_get_utf16_rfId" , "i_bs_get_utf16_xfId" , "i_bs_get_utf8_rfd" , "i_bs_get_utf8_xfd" , "i_bs_init_IId" , "i_bs_init_bits_IId" , "i_bs_init_bits_fail_rjId" , "i_bs_init_bits_fail_xjId" , "i_bs_init_bits_fail_yjId" , "i_bs_init_bits_fail_heap_IjId" , "i_bs_init_bits_heap_IIId" , "i_bs_init_fail_rjId" , "i_bs_init_fail_xjId" , "i_bs_init_fail_yjId" , "i_bs_init_fail_heap_IjId" , "i_bs_init_heap_IIId" , "i_bs_init_heap_bin_IId" , "i_bs_init_heap_bin_heap_IIId" , "i_bs_match_string_rfII" , "i_bs_match_string_xfII" , "i_bs_private_append_jId" , "i_bs_put_utf16_jIs" , "i_bs_put_utf8_js" , "i_bs_restore2_rI" , "i_bs_restore2_xI" , "i_bs_save2_rI" , "i_bs_save2_xI" , "i_bs_skip_bits2_frxI" , "i_bs_skip_bits2_fryI" , "i_bs_skip_bits2_fxrI" , "i_bs_skip_bits2_fxxI" , "i_bs_skip_bits2_fxyI" , "i_bs_skip_bits_all2_frI" , "i_bs_skip_bits_all2_fxI" , "i_bs_skip_bits_imm2_frI" , "i_bs_skip_bits_imm2_fxI" , "i_bs_start_match2_rfIId" , "i_bs_start_match2_xfIId" , "i_bs_start_match2_yfIId" , "i_bs_utf16_size_sd" , "i_bs_utf8_size_sd" , "i_bs_validate_unicode_js" , "i_bs_validate_unicode_retract_j" , "i_bsl_jId" , "i_bsr_jId" , "i_bxor_jId" , "i_call_f" , "i_call_ext_e" , "i_call_ext_last_eP" , "i_call_ext_only_e" , "i_call_fun_I" , "i_call_fun_last_IP" , "i_call_last_fP" , "i_call_only_f" , "i_debug_breakpoint" , "i_element_rjsd" , "i_element_xjsd" , "i_element_yjsd" , "i_fadd_lll" , "i_fast_element_rjId" , "i_fast_element_xjId" , "i_fast_element_yjId" , "i_fcheckerror" , "i_fdiv_lll" , "i_fetch_rx" , "i_fetch_ry" , "i_fetch_xr" , "i_fetch_xx" , "i_fetch_xy" , "i_fetch_yr" , "i_fetch_yx" , "i_fetch_yy" , "i_fetch_rc" , "i_fetch_xc" , "i_fetch_yc" , "i_fetch_cr" , "i_fetch_cx" , "i_fetch_cy" , "i_fetch_cc" , "i_fetch_ss" , "i_fmul_lll" , "i_fnegate_ll" , "i_fsub_lll" , "i_func_info_IaaI" , "i_gc_bif1_jIsId" , "i_gc_bif2_jIId" , "i_gc_bif3_jIsId" , "i_generic_breakpoint" , "i_get_sd" , "i_get_map_element_frar" , "i_get_map_element_frax" , "i_get_map_element_fray" , "i_get_map_element_frxr" , "i_get_map_element_frxx" , "i_get_map_element_frxy" , "i_get_map_element_fxar" , "i_get_map_element_fxax" , "i_get_map_element_fxay" , "i_get_map_element_fxxr" , "i_get_map_element_fxxx" , "i_get_map_element_fxxy" , "i_get_map_element_fyar" , "i_get_map_element_fyax" , "i_get_map_element_fyay" , "i_get_map_element_fyxr" , "i_get_map_element_fyxx" , "i_get_map_element_fyxy" , "i_get_map_elements_fsI" , "i_get_tuple_element_rPr" , "i_get_tuple_element_rPx" , "i_get_tuple_element_rPy" , "i_get_tuple_element_xPr" , "i_get_tuple_element_xPx" , "i_get_tuple_element_xPy" , "i_get_tuple_element_yPr" , "i_get_tuple_element_yPx" , "i_get_tuple_element_yPy" , "i_has_map_field_fra" , "i_has_map_field_frr" , "i_has_map_field_frx" , "i_has_map_field_fry" , "i_has_map_field_fxa" , "i_has_map_field_fxr" , "i_has_map_field_fxx" , "i_has_map_field_fxy" , "i_has_map_field_fya" , "i_has_map_field_fyr" , "i_has_map_field_fyx" , "i_has_map_field_fyy" , "i_has_map_fields_fsI" , "i_hibernate" , "i_increment_rIId" , "i_increment_xIId" , "i_increment_yIId" , "i_int_bnot_jsId" , "i_int_div_jId" , "i_is_eq_f" , "i_is_eq_exact_f" , "i_is_eq_exact_immed_frc" , "i_is_eq_exact_immed_fxc" , "i_is_eq_exact_immed_fyc" , "i_is_eq_exact_literal_rfc" , "i_is_eq_exact_literal_xfc" , "i_is_eq_exact_literal_yfc" , "i_is_ge_f" , "i_is_lt_f" , "i_is_ne_f" , "i_is_ne_exact_f" , "i_is_ne_exact_immed_frc" , "i_is_ne_exact_immed_fxc" , "i_is_ne_exact_immed_fyc" , "i_is_ne_exact_literal_rfc" , "i_is_ne_exact_literal_xfc" , "i_is_ne_exact_literal_yfc" , "i_jump_on_val_rfII" , "i_jump_on_val_xfII" , "i_jump_on_val_yfII" , "i_jump_on_val_zero_rfI" , "i_jump_on_val_zero_xfI" , "i_jump_on_val_zero_yfI" , "i_loop_rec_fr" , "i_m_div_jId" , "i_make_fun_It" , "i_minus_jId" , "i_move_call_crf" , "i_move_call_ext_cre" , "i_move_call_ext_last_ePcr" , "i_move_call_ext_only_ecr" , "i_move_call_last_fPcr" , "i_move_call_only_fcr" , "i_new_bs_put_binary_jsIs" , "i_new_bs_put_binary_all_jsI" , "i_new_bs_put_binary_imm_jIs" , "i_new_bs_put_float_jsIs" , "i_new_bs_put_float_imm_jIIs" , "i_new_bs_put_integer_jsIs" , "i_new_bs_put_integer_imm_jIIs" , "i_plus_jId" , "i_put_tuple_rI" , "i_put_tuple_xI" , "i_put_tuple_yI" , "i_recv_set" , "i_rem_jId" , "i_return_time_trace" , "i_return_to_trace" , "i_select_tuple_arity_rfI" , "i_select_tuple_arity_xfI" , "i_select_tuple_arity_yfI" , "i_select_tuple_arity2_rfAfAf" , "i_select_tuple_arity2_xfAfAf" , "i_select_tuple_arity2_yfAfAf" , "i_select_val_rfI" , "i_select_val_xfI" , "i_select_val_yfI" , "i_select_val2_rfcfcf" , "i_select_val2_xfcfcf" , "i_select_val2_yfcfcf" , "i_times_jId" , "i_trim_I" , "i_wait_error" , "i_wait_error_locked" , "i_wait_timeout_fI" , "i_wait_timeout_fs" , "i_wait_timeout_locked_fI" , "i_wait_timeout_locked_fs" , "i_yield" , "if_end" , "init_y" , "init2_yy" , "init3_yyy" , "int_code_end" , "is_atom_fr" , "is_atom_fx" , "is_atom_fy" , "is_binary_fr" , "is_binary_fx" , "is_binary_fy" , "is_bitstring_fr" , "is_bitstring_fx" , "is_bitstring_fy" , "is_boolean_fr" , "is_boolean_fx" , "is_boolean_fy" , "is_float_fr" , "is_float_fx" , "is_float_fy" , "is_function_fr" , "is_function_fx" , "is_function_fy" , "is_function2_fss" , "is_integer_fr" , "is_integer_fx" , "is_integer_fy" , "is_integer_allocate_frII" , "is_integer_allocate_fxII" , "is_list_fr" , "is_list_fx" , "is_list_fy" , "is_map_fr" , "is_map_fx" , "is_map_fy" , "is_nil_fr" , "is_nil_fx" , "is_nil_fy" , "is_non_empty_list_test_heap_frIt" , "is_nonempty_list_fr" , "is_nonempty_list_fx" , "is_nonempty_list_fy" , "is_nonempty_list_allocate_frIt" , "is_nonempty_list_allocate_fxIt" , "is_number_fr" , "is_number_fx" , "is_number_fy" , "is_pid_fr" , "is_pid_fx" , "is_pid_fy" , "is_port_fr" , "is_port_fx" , "is_port_fy" , "is_reference_fr" , "is_reference_fx" , "is_reference_fy" , "is_tuple_fr" , "is_tuple_fx" , "is_tuple_fy" , "is_tuple_of_arity_frA" , "is_tuple_of_arity_fxA" , "is_tuple_of_arity_fyA" , "jump_f" , "label_L" , "line_I" , "loop_rec_end_f" , "move_nr" , "move_nx" , "move_rx" , "move_ry" , "move_xr" , "move_xx" , "move_xy" , "move_yr" , "move_yx" , "move_yy" , "move_cr" , "move_cx" , "move2_xxxx" , "move2_xyxy" , "move2_yxyx" , "move_call_xrf" , "move_call_yrf" , "move_call_last_xrfQ" , "move_call_last_yrfQ" , "move_call_only_xrf" , "move_deallocate_return_nrQ" , "move_deallocate_return_xrQ" , "move_deallocate_return_yrQ" , "move_deallocate_return_crQ" , "move_jump_fn" , "move_jump_fx" , "move_jump_fy" , "move_jump_fc" , "move_return_nr" , "move_return_xr" , "move_return_cr" , "move_x1_c" , "move_x2_c" , "new_map_jdII" , "node_r" , "node_x" , "node_y" , "normal_exit" , "on_load" , "put_list_rnx" , "put_list_rxr" , "put_list_rxx" , "put_list_ryx" , "put_list_xnx" , "put_list_xrr" , "put_list_xrx" , "put_list_xxr" , "put_list_xxx" , "put_list_xyr" , "put_list_xyx" , "put_list_ynx" , "put_list_yrr" , "put_list_yrx" , "put_list_yxr" , "put_list_yxx" , "put_list_yyr" , "put_list_yyx" , "put_list_rcr" , "put_list_rcx" , "put_list_rcy" , "put_list_xcr" , "put_list_xcx" , "put_list_xcy" , "put_list_ycr" , "put_list_ycx" , "put_list_ycy" , "put_list_crr" , "put_list_crx" , "put_list_cry" , "put_list_cxr" , "put_list_cxx" , "put_list_cxy" , "put_list_cyr" , "put_list_cyx" , "put_list_cyy" , "put_list_ssd" , "raise_ss" , "recv_mark_f" , "remove_message" , "return" , "return_trace" , "self_r" , "self_x" , "self_y" , "send" , "set_tuple_element_sdP" , "system_limit_j" , "test_arity_frA" , "test_arity_fxA" , "test_arity_fyA" , "test_heap_It" , "test_heap_1_put_list_Iy" , "timeout" , "timeout_locked" , "try_case_end_s" , "try_end_y" , "update_map_assoc_jsdII" , "update_map_exact_jsdII" , "wait_f" , "wait_locked_f" , "wait_unlocked_f" ] ok 2> |
回顾 [Erlang 0010] Erlang 热更新 [ 链接 ],看看两种调用时候为什么m:f(a)会触发热更新,测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | loop() -> receive code_switch -> m:loop(); Msg -> io:format( "." ), loop() end. %% t.S文件 {function, loop, 0, 9}. {label,8}. {line,[{location, "t.erl" ,14}]}. {func_info,{atom,t},{atom,loop},0}. {label,9}. {allocate,0,0}. {line,[{location, "t.erl" ,15}]}. {label,10}. {loop_rec,{f,12},{x,0}}. {test,is_eq_exact,{f,11},[{x,0},{atom,code_switch}]}. remove_message. {line,[{location, "t.erl" ,17}]}. {call_ext_last,0,{extfunc,m,loop,0},0}. {label,11}. remove_message. {move,{literal, "." },{x,0}}. {line,[{location, "t.erl" ,19}]}. {call_ext,1,{extfunc,io,format,1}}. {call_last,0,{f,9},0}. {label,12}. {wait,{f,10}}. %% t.dis文件 00007F9D864F6C38: i_func_info_IaaI 0 t loop 0 00007F9D864F6C60: allocate_tt 0 0 00007F9D864F6C70: i_loop_rec_fr f(00007F9D864F6CF0) x(0) 00007F9D864F6C80: i_is_eq_exact_immed_frc f(00007F9D864F6CB8) x(0) code_switch 00007F9D864F6C98: remove_message 00007F9D864F6CA0: i_call_ext_last_eP m:loop/0 0 00007F9D864F6CB8: remove_message 00007F9D864F6CC0: i_move_call_ext_cre "." x(0) io:format/1 00007F9D864F6CD8: i_call_last_fP t:loop/0 0 00007F9D864F6CF0: wait_f f(00007F9D864F6C70) |
然后,使用这个方法,我把ErlangEfficiency Guide 里面大部分代码都做了一下测试,目的是想从一个新的角度切入以前的问题,其中一个比较有意思的是:
1 2 3 4 5 6 7 8 9 10 11 12 | bin()-> Bin0 = <<0>>, %% 1 Bin1 = <<Bin0/binary,1,2,3>>, %% 2 Bin2 = <<Bin1/binary,4,5,6>>, %% 3 Bin3 = <<Bin2/binary,7,8,9>>, %% 4 Bin4 = <<Bin1/binary,17>>, %% 5 !!! {Bin4,Bin3}. %% 6 |
这个方法由于中间的变量都是已知的,编译之后直接使用运算结果了:
1 2 3 4 5 6 7 | {function, bin, 0, 14}. {label,13}. {line,[{location, "t.erl" ,25}]}. {func_info,{atom,t},{atom,bin},0}. {label,14}. {move,{literal,{<<0,1,2,3,17>>,<<0,1,2,3,4,5,6,7,8,9>>}},{x,0}}. return . |
印象比较深刻的是 http://www.erlang.org/doc/efficiency_guide/functions.html#id67136 里面提到函数分支的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | atom_map1(one) -> 1; atom_map1(two) -> 2; atom_map1(three) -> 3; atom_map1(Int) when is_integer(Int) -> Int; atom_map1(four) -> 4; atom_map1(five) -> 5; atom_map1(six) -> 6. atom_map2(one) -> 1; atom_map2(two) -> 2; atom_map2(three) -> 3; atom_map2(four) -> 4; atom_map2(five) -> 5; atom_map2(six) -> 6; atom_map2(Int) when is_integer(Int) -> Int. atom_map3(Int) when is_integer(Int) -> Int; atom_map3(one) -> 1; atom_map3(two) -> 2; atom_map3(three) -> 3; atom_map3(four) -> 4; atom_map3(five) -> 5; atom_map3(six) -> 6. |
对于atom_map1为什么有问题文档上是这样说的:
First the input value is compared to one, two, and three (using a single instruction that does a binary search; thus, quite efficient even if there are many values) to select which one of the first three clauses to execute (if any).
If none of the first three clauses matched, the fourth clause will match since a variable always matches. If the guard test is_integer(Int) succeeds, the fourth clause will be executed.
If the guard test failed, the input value is compared to four, five, and six, and the appropriate clause is selected. (There will be a function_clause exception if none of the values matched.)
二分查找,优化等等,虽然道理能明白,但是具体到细节就有点恍惚,看下t.S文件就明白了,代码太长被我折叠了,所谓的二分查找就是select_val指令实现的,可以看到atom_map2,atom_map3都是将所有的分支放在一起做二分查找,而atom_map1的执行过程被分成了三段;再回头看上面的说明,是不是就清晰多了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | {function, atom_map1, 1, 33}. {label,32}. {line,[{location, "t.erl" ,77}]}. {func_info,{atom,t},{atom,atom_map1},1}. {label,33}. {test,is_atom,{f,37},[{x,0}]}. {select_val,{x,0}, {f,37}, {list,[{atom,three}, {f,34}, {atom,two}, {f,35}, {atom,one}, {f,36}]}}. {label,34}. {move,{integer,3},{x,0}}. return . {label,35}. {move,{integer,2},{x,0}}. return . {label,36}. {move,{integer,1},{x,0}}. return . {label,37}. {test,is_integer,{f,38},[{x,0}]}. return . {label,38}. {test,is_atom,{f,32},[{x,0}]}. {select_val,{x,0}, {f,32}, {list,[{atom,six}, {f,39}, {atom,five}, {f,40}, {atom,four}, {f,41}]}}. {label,39}. {move,{integer,6},{x,0}}. return . {label,40}. {move,{integer,5},{x,0}}. return . {label,41}. {move,{integer,4},{x,0}}. return . {function, atom_map2, 1, 43}. {label,42}. {line,[{location, "t.erl" ,87}]}. {func_info,{atom,t},{atom,atom_map2},1}. {label,43}. {test,is_atom,{f,50},[{x,0}]}. {select_val,{x,0}, {f,50}, {list,[{atom,six}, {f,44}, {atom,five}, {f,45}, {atom,four}, {f,46}, {atom,three}, {f,47}, {atom,two}, {f,48}, {atom,one}, {f,49}]}}. {label,44}. {move,{integer,6},{x,0}}. return . {label,45}. {move,{integer,5},{x,0}}. return . {label,46}. {move,{integer,4},{x,0}}. return . {label,47}. {move,{integer,3},{x,0}}. return . {label,48}. {move,{integer,2},{x,0}}. return . {label,49}. {move,{integer,1},{x,0}}. return . {label,50}. {test,is_integer,{f,42},[{x,0}]}. return . {function, atom_map3, 1, 52}. {label,51}. {line,[{location, "t.erl" ,97}]}. {func_info,{atom,t},{atom,atom_map3},1}. {label,52}. {test,is_integer,{f,53},[{x,0}]}. return . {label,53}. {test,is_atom,{f,51},[{x,0}]}. {select_val,{x,0}, {f,51}, {list,[{atom,six}, {f,54}, {atom,five}, {f,55}, {atom,four}, {f,56}, {atom,three}, {f,57}, {atom,two}, {f,58}, {atom,one}, {f,59}]}}. {label,54}. {move,{integer,6},{x,0}}. return . {label,55}. {move,{integer,5},{x,0}}. return . {label,56}. {move,{integer,4},{x,0}}. return . {label,57}. {move,{integer,3},{x,0}}. return . {label,58}. {move,{integer,2},{x,0}}. return . {label,59}. {move,{integer,1},{x,0}}. return . |
我非常喜欢汇丰银行的一句宣传语"不同的视角,打开迥然不同的世界",使用不同的视角对同一个技术问题也会有不同的理解.
参考资料:
[0] A Peek Inside the Erlang Compiler http://prog21.dadgum.com/127.html
[1] Erlang源码汇编格式 http://blog.yufeng.info/archives/498
[2] Erlang代码反编译以及查看汇编码 http://blog.yufeng.info/archives/1599
[3] 实验Erlang语法对应的opcode 让你对erlang理解更深 http://blog.yufeng.info/archives/34
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2008-08-28 Interaction design pattern