diff options
Diffstat (limited to 'lib/compiler/src')
| -rw-r--r-- | lib/compiler/src/beam_validator.erl | 248 | 
1 files changed, 16 insertions, 232 deletions
| diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 1f2448d433..e60184c929 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -22,7 +22,6 @@  %% Avoid warning for local function error/1 clashing with autoimported BIF.  -compile({no_auto_import,[error/1]}). --export([file/1, files/1]).  %% Interface for compiler.  -export([module/2, format_error/1]). @@ -40,38 +39,12 @@  -define(DBG_FORMAT(F, D), ok).  -endif. -%%% -%%% API functions. -%%% - --spec file(file:filename()) -> 'ok' | {'error', term()}. - -file(Name) when is_list(Name) -> -    case case filename:extension(Name) of -	     ".S" -> s_file(Name); -	     ".beam" -> beam_file(Name) -	 end of -	[] -> ok; -	Es -> {error,Es} -    end. - --spec files([file:filename()]) -> 'ok'. - -files([F|Fs]) -> -    ?DBG_FORMAT("# Verifying: ~p~n", [F]), -    case file(F) of -	ok -> ok; -	{error,Es} ->  -	    io:format("~tp:~n~ts~n", [F,format_error(Es)]) -    end, -    files(Fs); -files([]) -> ok. -  %% To be called by the compiler.  module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts)    when is_atom(Mod), is_list(Exp), is_list(Attr), is_integer(Lc) ->      case validate(Mod, Fs) of -	[] -> {ok,Code}; +	[] -> +	    {ok,Code};  	Es0 ->  	    Es = [{?MODULE,E} || E <- Es0],  	    {error,[{atom_to_list(Mod),Es}]} @@ -79,12 +52,6 @@ module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts)  -spec format_error(term()) -> iolist(). -format_error([]) -> []; -format_error([{{M,F,A},{I,Off,Desc}}|Es]) -> -    [io_lib:format("  ~p:~p/~p+~p:~n    ~p - ~p~n",  -		   [M,F,A,Off,I,Desc])|format_error(Es)]; -format_error([Error|Es]) -> -    [format_error(Error)|format_error(Es)];  format_error({{_M,F,A},{I,Off,limit}}) ->      io_lib:format(        "function ~p/~p+~p:~n" @@ -103,8 +70,6 @@ format_error({{_M,F,A},{I,Off,Desc}}) ->        "  Internal consistency check failed - please report this bug.~n"        "  Instruction: ~p~n"        "  Error:       ~p:~n", [F,A,Off,I,Desc]); -format_error({Module,Error}) -> -    [Module:format_error(Error)];  format_error(Error) ->      io_lib:format("~p~n", [Error]). @@ -112,36 +77,6 @@ format_error(Error) ->  %%% Local functions follow.  %%%  -s_file(Name) -> -    {ok,Is} = file:consult(Name), -    {module,Module} = lists:keyfind(module, 1, Is), -    Fs = find_functions(Is), -    validate(Module, Fs). - -find_functions(Fs) -> -    find_functions_1(Fs, none, [], []). - -find_functions_1([{function,Name,Arity,Entry}|Is], Func, FuncAcc, Acc0) -> -    Acc = add_func(Func, FuncAcc, Acc0), -    find_functions_1(Is, {Name,Arity,Entry}, [], Acc); -find_functions_1([I|Is], Func, FuncAcc, Acc) -> -    find_functions_1(Is, Func, [I|FuncAcc], Acc); -find_functions_1([], Func, FuncAcc, Acc) -> -    reverse(add_func(Func, FuncAcc, Acc)). - -add_func(none, _, Acc) -> Acc; -add_func({Name,Arity,Entry}, Is, Acc) -> -    [{function,Name,Arity,Entry,reverse(Is)}|Acc]. - -beam_file(Name) -> -    try beam_disasm:file(Name) of -	{error,beam_lib,Reason} -> [{beam_lib,Reason}]; -	#beam_file{module=Module, code=Code0} -> -	    Code = normalize_disassembled_code(Code0), -	    validate(Module, Code) -    catch _:_ -> [disassembly_failed] -    end. -  %%%  %%% The validator follows.  %%% @@ -196,23 +131,16 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->      try validate_1(Code, Name, Ar, Entry, Ft) of  	_ -> validate_0(Module, Fs, Ft)      catch -	Error -> +	throw:Error -> +	    %% Controlled error.  	    [Error|validate_0(Module, Fs, Ft)]; -	  error:Error -> -	    [validate_error(Error, Module, Name, Ar)|validate_0(Module, Fs, Ft)] +	Class:Error -> +	    %% Crash. +	    Stack = erlang:get_stacktrace(), +	    io:fwrite("Function: ~w/~w\n", [Name,Ar]), +	    erlang:raise(Class, Error, Stack)      end. --ifdef(DEBUG). -validate_error(Error, Module, Name, Ar) -> -    exit(validate_error_1(Error, Module, Name, Ar)). --else. -validate_error(Error, Module, Name, Ar) -> -    validate_error_1(Error, Module, Name, Ar). --endif. -validate_error_1(Error, Module, Name, Ar) -> -    {{Module,Name,Ar}, -     {internal_error,'_',{Error,erlang:get_stacktrace()}}}. -  -type index() :: non_neg_integer().  -type reg_tab() :: gb_trees:tree(index(), 'none' | {'value', _}). @@ -225,7 +153,6 @@ validate_error_1(Error, Module, Name, Ar) ->  	 hf=0,				%Available heap size for floats.  	 fls=undefined,			%Floating point state.  	 ct=[],				%List of hot catch/try labels -	 bsm=undefined,			%Bit syntax matching state.  	 bits=undefined,	        %Number of bits in bit syntax binary.  	 setelem=false			%Previous instruction was setelement/3.  	}). @@ -308,7 +235,7 @@ labels_1([{label,L}|Is], R) ->  labels_1([{line,_}|Is], R) ->      labels_1(Is, R);  labels_1(Is, R) -> -    {lists:reverse(R),Is}. +    {reverse(R),Is}.  init_state(Arity) ->      Xs = init_regs(Arity, term), @@ -403,10 +330,6 @@ valfun_1({init,{y,_}=Reg}, Vst) ->      set_type_y(initialized, Reg, Vst);  valfun_1({test_heap,Heap,Live}, Vst) ->      test_heap(Heap, Live, Vst); -valfun_1({bif,_Op,nofail,Src,Dst}, Vst) -> -    %% The 'nofail' atom only occurs in disassembled code. -    validate_src(Src, Vst), -    set_type_reg(term, Dst, Vst);  valfun_1({bif,Op,{f,_},Src,Dst}=I, Vst) ->      case is_bif_safe(Op, length(Src)) of  	false -> @@ -432,9 +355,6 @@ valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->  valfun_1({put,Src}, Vst) ->      assert_term(Src, Vst),      eat_heap(1, Vst); -valfun_1({put_string,Sz,_,Dst}, Vst0) when is_integer(Sz) -> -    Vst = eat_heap(2*Sz, Vst0), -    set_type_reg(cons, Dst, Vst);  %% Instructions for optimization of selective receives.  valfun_1({recv_mark,{f,Fail}}, Vst) when is_integer(Fail) ->      Vst; @@ -602,8 +522,6 @@ valfun_4({call_ext_last,Live,Func,StkSize},      tail_call(Func, Live, Vst);  valfun_4({call_ext_last,_,_,_}, #vst{current=#st{numy=NumY}}) ->      error({allocated,NumY}); -valfun_4({make_fun,_,_,Live}, Vst) -> -    call('fun', Live, Vst);  valfun_4({make_fun2,_,_,_,Live}, Vst) ->      call(make_fun, Live, Vst);  %% Other BIFs @@ -620,8 +538,6 @@ valfun_4({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) ->      TupleType = upgrade_tuple_type({tuple,[get_tuple_size(PosType)]}, TupleType0),      Vst = set_type(TupleType, Tuple, Vst1),      set_type_reg(term, Dst, Vst); -valfun_4({raise,{f,_}=Fail,Src,Dst}, Vst) -> -    valfun_4({bif,raise,Fail,Src,Dst}, Vst);  valfun_4({bif,Op,{f,Fail},Src,Dst}, Vst0) ->      validate_src(Src, Vst0),      Vst = branch_state(Fail, Vst0), @@ -738,32 +654,6 @@ valfun_4({bs_save2,Ctx,SavePoint}, Vst) ->  valfun_4({bs_restore2,Ctx,SavePoint}, Vst) ->      bsm_restore(Ctx, SavePoint, Vst); -%% Bit syntax instructions. -valfun_4({bs_start_match,{f,_Fail}=F,Src}, Vst) -> -    valfun_4({test,bs_start_match,F,[Src]}, Vst); -valfun_4({test,bs_start_match,{f,Fail},[Src]}, Vst) -> -    assert_term(Src, Vst), -    bs_start_match(branch_state(Fail, Vst)); - -valfun_4({bs_save,SavePoint}, Vst) -> -    bs_assert_state(Vst), -    bs_save(SavePoint, Vst); -valfun_4({bs_restore,SavePoint}, Vst) -> -    bs_assert_state(Vst), -    bs_assert_savepoint(SavePoint, Vst), -    Vst; -valfun_4({test,bs_skip_bits,{f,Fail},[Src,_,_]}, Vst) -> -    bs_assert_state(Vst), -    assert_term(Src, Vst), -    branch_state(Fail, Vst); -valfun_4({test,bs_test_tail,{f,Fail},_}, Vst) -> -    bs_assert_state(Vst), -    branch_state(Fail, Vst); -valfun_4({test,_,{f,Fail},[_,_,_,Dst]}, Vst0) -> -    bs_assert_state(Vst0), -    Vst = branch_state(Fail, Vst0), -    set_type_reg({integer,[]}, Dst, Vst); -  %% Other test instructions.  valfun_4({test,is_float,{f,Lbl},[Float]}, Vst) ->      assert_term(Float, Vst), @@ -795,9 +685,6 @@ valfun_4({bs_utf8_size,{f,Fail},A,Dst}, Vst) ->  valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->      assert_term(A, Vst),      set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst)); -valfun_4({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst) -> -    assert_term(Src, Vst), -    set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst));  valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->      verify_live(Live, Vst0),      if @@ -868,16 +755,6 @@ valfun_4({bs_put_utf32,{f,Fail},_,Src}=I, Vst0) ->      assert_term(Src, Vst0),      Vst = bs_align_check(I, Vst0),      branch_state(Fail, Vst); -%% Old bit syntax construction (before R10B). -valfun_4({bs_init,_,_}, Vst) -> -    bs_zero_bits(Vst); -valfun_4({bs_need_buf,_}, Vst) -> Vst; -valfun_4({bs_final,{f,Fail},Dst}, Vst0) -> -    Vst = branch_state(Fail, Vst0), -    set_type_reg(binary, Dst, Vst); -valfun_4({bs_final2,Src,Dst}, Vst0) -> -    assert_term(Src, Vst0), -    set_type_reg(binary, Dst, Vst0);  %% Map instructions.  valfun_4({put_map_assoc,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->      verify_put_map(Fail, Src, Dst, Live, List, Vst); @@ -940,9 +817,6 @@ validate_bs_skip_utf(Fail, Ctx, Live, Vst0) ->  %%  val_dsetel({move,_,_}, Vst) ->      Vst; -val_dsetel({put_string,0,{string,""},_}, Vst) -> -    %% An empty string is OK since it doesn't build anything. -    Vst;  val_dsetel({call_ext,3,{extfunc,erlang,setelement,3}}, #vst{current=St}=Vst) ->      Vst#vst{current=St#st{setelem=true}};  val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=false}}) -> @@ -976,7 +850,7 @@ call(Name, Live, #vst{current=St}=Vst) ->  	Type when Type =/= exception ->  	    %% Type is never 'exception' because it has been handled earlier.  	    Xs = gb_trees_from_list([{0,Type}]), -	    Vst#vst{current=St#st{x=Xs,f=init_fregs(),bsm=undefined}} +	    Vst#vst{current=St#st{x=Xs,f=init_fregs()}}      end.  %% Tail call. @@ -1034,7 +908,7 @@ allocate(_, _, _, _, #vst{current=#st{numy=Numy}}) ->      error({existing_stack_frame,{size,Numy}}).  deallocate(#vst{current=St}=Vst) -> -    Vst#vst{current=St#st{y=init_regs(0, initialized),numy=none,bsm=undefined}}. +    Vst#vst{current=St#st{y=init_regs(0, initialized),numy=none}}.  test_heap(Heap, Live, Vst0) ->      verify_live(Live, Vst0), @@ -1042,7 +916,7 @@ test_heap(Heap, Live, Vst0) ->      heap_alloc(Heap, Vst).  heap_alloc(Heap, #vst{current=St0}=Vst) -> -    St1 = kill_heap_allocation(St0#st{bsm=undefined}), +    St1 = kill_heap_allocation(St0),      St = heap_alloc_1(Heap, St1),      Vst#vst{current=St}. @@ -1152,42 +1026,6 @@ check_strict_value_termorder([V1|[V2|_]=Vs]) ->  check_strict_value_termorder([_]) -> true.  %%% -%%% Binary matching. -%%% -%%% Possible values for the bsm field (=bit syntax matching state). -%%% -%%% undefined 	- Undefined (initial state). No matching instructions allowed. -%%%		  -%%% (gb set)	- The gb set contains the defined save points. -%%% -%%% The bsm field is reset to 'undefined' by instructions that may cause a -%%% a garbage collection (might move the binary) and/or context switch -%%% (may invalidate the save points). - -bs_start_match(#vst{current=#st{bsm=undefined}=St}=Vst) -> -    Vst#vst{current=St#st{bsm=gb_sets:empty()}}; -bs_start_match(Vst) -> -    %% Must retain save points here - it is possible to restore back -    %% to a previous binary. -    Vst. - -bs_save(Reg, #vst{current=#st{bsm=Saved}=St}=Vst) -  when is_integer(Reg), Reg < ?MAXREG  -> -    Vst#vst{current=St#st{bsm=gb_sets:add(Reg, Saved)}}; -bs_save(_, _) -> error(limit). - -bs_assert_savepoint(Reg, #vst{current=#st{bsm=Saved}}) -> -    case gb_sets:is_member(Reg, Saved) of -	false -> error({no_save_point,Reg}); -	true -> ok -    end. - -bs_assert_state(#vst{current=#st{bsm=undefined}}) -> -    error(no_bs_match_state); -bs_assert_state(_) -> ok. - - -%%%  %%% New binary matching instructions.  %%% @@ -1521,14 +1359,13 @@ merge_states(L, St, Branched) when L =/= 0 ->  	{value,OtherSt} -> merge_states_1(St, OtherSt)      end. -merge_states_1(#st{x=Xs0,y=Ys0,numy=NumY0,h=H0,ct=Ct0,bsm=Bsm0}=St, -	       #st{x=Xs1,y=Ys1,numy=NumY1,h=H1,ct=Ct1,bsm=Bsm1}) -> +merge_states_1(#st{x=Xs0,y=Ys0,numy=NumY0,h=H0,ct=Ct0}=St, +	       #st{x=Xs1,y=Ys1,numy=NumY1,h=H1,ct=Ct1}) ->      NumY = merge_stk(NumY0, NumY1),      Xs = merge_regs(Xs0, Xs1),      Ys = merge_y_regs(Ys0, Ys1),      Ct = merge_ct(Ct0, Ct1), -    Bsm = merge_bsm(Bsm0, Bsm1), -    St#st{x=Xs,y=Ys,numy=NumY,h=min(H0, H1),ct=Ct,bsm=Bsm}. +    St#st{x=Xs,y=Ys,numy=NumY,h=min(H0, H1),ct=Ct}.  merge_stk(S, S) -> S;  merge_stk(_, _) -> undecided. @@ -1611,10 +1448,6 @@ merge_types(T1, T2) when T1 =/= T2 ->      %% Too different. All we know is that the type is a 'term'.      term. -merge_bsm(undefined, _) -> undefined; -merge_bsm(_, undefined) -> undefined; -merge_bsm(Bsm0, Bsm1) -> gb_sets:intersection(Bsm0, Bsm1). -  tuple_sz([Sz]) -> Sz;  tuple_sz(Sz) -> Sz. @@ -1836,52 +1669,3 @@ error(Error) -> exit(Error).  -else.  error(Error) -> throw(Error).  -endif. - - -%%% -%%% Rewrite disassembled code to the same format as we used internally -%%% to not have to worry later. -%%% - -normalize_disassembled_code(Fs) -> -    Index = ndc_index(Fs, []), -    ndc(Fs, Index, []). - -ndc_index([{function,Name,Arity,Entry,_Code}|Fs], Acc) -> -    ndc_index(Fs, [{{Name,Arity},Entry}|Acc]); -ndc_index([], Acc) -> -    gb_trees:from_orddict(lists:sort(Acc)). - -ndc([{function,Name,Arity,Entry,Code0}|Fs], D, Acc) -> -    Code = ndc_1(Code0, D, []), -    ndc(Fs, D, [{function,Name,Arity,Entry,Code}|Acc]); -ndc([], _, Acc) -> reverse(Acc). -     -ndc_1([{call=Op,A,{_,F,A}}|Is], D, Acc) -> -    ndc_1(Is, D, [{Op,A,{f,gb_trees:get({F,A}, D)}}|Acc]); -ndc_1([{call_only=Op,A,{_,F,A}}|Is], D, Acc) -> -    ndc_1(Is, D, [{Op,A,{f,gb_trees:get({F,A}, D)}}|Acc]); -ndc_1([{call_last=Op,A,{_,F,A},Sz}|Is], D, Acc) -> -    ndc_1(Is, D, [{Op,A,{f,gb_trees:get({F,A}, D)},Sz}|Acc]); -ndc_1([{arithbif,Op,F,Src,Dst}|Is], D, Acc) -> -    ndc_1(Is, D, [{bif,Op,F,Src,Dst}|Acc]); -ndc_1([{arithfbif,Op,F,Src,Dst}|Is], D, Acc) -> -    ndc_1(Is, D, [{bif,Op,F,Src,Dst}|Acc]); -ndc_1([{test,bs_start_match2=Op,F,[A1,Live,A3,Dst]}|Is], D, Acc) -> -    ndc_1(Is, D, [{test,Op,F,Live,[A1,A3],Dst}|Acc]); -ndc_1([{test,bs_get_binary2=Op,F,[A1,Live,A3,A4,A5,Dst]}|Is], D, Acc) -> -    ndc_1(Is, D, [{test,Op,F,Live,[A1,A3,A4,A5],Dst}|Acc]); -ndc_1([{test,bs_get_float2=Op,F,[A1,Live,A3,A4,A5,Dst]}|Is], D, Acc) -> -    ndc_1(Is, D, [{test,Op,F,Live,[A1,A3,A4,A5],Dst}|Acc]); -ndc_1([{test,bs_get_integer2=Op,F,[A1,Live,A3,A4,A5,Dst]}|Is], D, Acc) -> -    ndc_1(Is, D, [{test,Op,F,Live,[A1,A3,A4,A5],Dst}|Acc]); -ndc_1([{test,bs_get_utf8=Op,F,[A1,Live,A3,Dst]}|Is], D, Acc) -> -    ndc_1(Is, D, [{test,Op,F,Live,[A1,A3],Dst}|Acc]); -ndc_1([{test,bs_get_utf16=Op,F,[A1,Live,A3,Dst]}|Is], D, Acc) -> -    ndc_1(Is, D, [{test,Op,F,Live,[A1,A3],Dst}|Acc]); -ndc_1([{test,bs_get_utf32=Op,F,[A1,Live,A3,Dst]}|Is], D, Acc) -> -    ndc_1(Is, D, [{test,Op,F,Live,[A1,A3],Dst}|Acc]); -ndc_1([I|Is], D, Acc) -> -    ndc_1(Is, D, [I|Acc]); -ndc_1([], _, Acc) -> -    reverse(Acc). | 
