diff options
Diffstat (limited to 'lib/compiler')
29 files changed, 422 insertions, 144 deletions
| diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 830c89ae84..522c1dc411 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -395,6 +395,14 @@ module.beam: module.erl \  	  <code>-compile({no_auto_import,[error/1]}).</code>  	  </item> +          <tag><c>no_line_info</c></tag> + +          <item> +            <p>Omit line number information in order to produce a slightly +	      smaller output file. +	    </p> +          </item> +          </taglist>          <p>If warnings are turned on (the <c>report_warnings</c> option diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl index 89d64834cf..6e63c4d0f2 100644 --- a/lib/compiler/src/beam_asm.erl +++ b/lib/compiler/src/beam_asm.erl @@ -23,7 +23,7 @@  -export([module/4]).  -export([encode/2]). --import(lists, [map/2,member/2,keymember/3,duplicate/2]). +-import(lists, [map/2,member/2,keymember/3,duplicate/2,splitwith/2]).  -include("beam_opcodes.hrl").  module(Code, Abst, SourceFile, Opts) -> @@ -31,22 +31,20 @@ module(Code, Abst, SourceFile, Opts) ->  assemble({Mod,Exp,Attr0,Asm0,NumLabels}, Abst, SourceFile, Opts) ->      {1,Dict0} = beam_dict:atom(Mod, beam_dict:new()), +    {0,Dict1} = beam_dict:fname(atom_to_list(Mod) ++ ".erl", Dict0),      NumFuncs = length(Asm0),      {Asm,Attr} = on_load(Asm0, Attr0), -    {Code,Dict1} = assemble_1(Asm, Exp, Dict0, []), -    build_file(Code, Attr, Dict1, NumLabels, NumFuncs, Abst, SourceFile, Opts). +    {Code,Dict2} = assemble_1(Asm, Exp, Dict1, []), +    build_file(Code, Attr, Dict2, NumLabels, NumFuncs, Abst, SourceFile, Opts).  on_load(Fs0, Attr0) ->      case proplists:get_value(on_load, Attr0) of  	undefined ->  	    {Fs0,Attr0};  	[{Name,0}] -> -	    Fs = map(fun({function,N,0,Entry,Asm0}) when N =:= Name -> -			     [{label,_}=L, -			      {func_info,_,_,_}=Fi, -			      {label,_}=E|Asm1] = Asm0, -			     Asm = [L,Fi,E,on_load|Asm1], -			     {function,N,0,Entry,Asm}; +	    Fs = map(fun({function,N,0,Entry,Is0}) when N =:= Name -> +			     Is = insert_on_load_instruction(Is0, Entry), +			     {function,N,0,Entry,Is};  			(F) ->  			     F  		     end, Fs0), @@ -54,6 +52,13 @@ on_load(Fs0, Attr0) ->  	    {Fs,Attr}      end. +insert_on_load_instruction(Is0, Entry) -> +    {Bef,[{label,Entry}=El|Is]} = +	splitwith(fun({label,L}) when L =:= Entry -> false; +		     (_) -> true +		  end, Is0), +    Bef ++ [El,on_load|Is]. +  assemble_1([{function,Name,Arity,Entry,Asm}|T], Exp, Dict0, Acc) ->      Dict1 = case member({Name,Arity}, Exp) of  		true -> @@ -132,7 +137,10 @@ build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) ->  			   LitTab = iolist_to_binary(zlib:compress(LitTab2)),  			   chunk(<<"LitT">>, <<(byte_size(LitTab2)):32>>, LitTab)  		   end, + +    %% Create the line chunk. +    LineChunk = chunk(<<"Line">>, build_line_table(Dict)),      %% Create the attributes and compile info chunks. @@ -150,8 +158,11 @@ build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) ->      %% Create IFF chunk.      Chunks = case member(slim, Opts) of -		 true -> [Essentials,AttrChunk,AbstChunk]; -		 false -> [Essentials,LocChunk,AttrChunk,CompileChunk,AbstChunk] +		 true -> +		     [Essentials,AttrChunk,AbstChunk]; +		 false -> +		     [Essentials,LocChunk,AttrChunk, +		      CompileChunk,AbstChunk,LineChunk]  	     end,      build_form(<<"BEAM">>, Chunks). @@ -201,6 +212,31 @@ build_attributes(Opts, SourceFile, Attr, Essentials) ->      Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc],      {term_to_binary(calc_vsn(Attr, Essentials)),term_to_binary(Compile)}. +build_line_table(Dict) -> +    {NumLineInstrs,NumFnames0,Fnames0,NumLines,Lines0} = +	beam_dict:line_table(Dict), +    NumFnames = NumFnames0 - 1, +    [_|Fnames1] = Fnames0, +    Fnames2 = [unicode:characters_to_binary(F) || F <- Fnames1], +    Fnames = << <<(byte_size(F)):16,F/binary>> || F <- Fnames2 >>, +    Lines1 = encode_line_items(Lines0, 0), +    Lines = iolist_to_binary(Lines1), +    Ver = 0, +    Bits = 0, +    <<Ver:32,Bits:32,NumLineInstrs:32,NumLines:32,NumFnames:32, +     Lines/binary,Fnames/binary>>. + +%% encode_line_items([{FnameIndex,Line}], PrevFnameIndex) +%%  Encode the line items compactly. Tag the FnameIndex with +%%  an 'a' tag (atom) and only include it when it has changed. +%%  Tag the line numbers with an 'i' (integer) tag. + +encode_line_items([{F,L}|T], F) -> +    [encode(?tag_i, L)|encode_line_items(T, F)]; +encode_line_items([{F,L}|T], _) -> +    [encode(?tag_a, F),encode(?tag_i, L)|encode_line_items(T, F)]; +encode_line_items([], _) -> []. +  %%  %% If the attributes contains no 'vsn' attribute, we'll insert one  %% with an MD5 "checksum" calculated on the code as its value. @@ -243,6 +279,9 @@ bif_type(_, 2)      -> bif2.  make_op({'%',_}, Dict) ->      {[],Dict}; +make_op({line,Location}, Dict0) -> +    {Index,Dict} = beam_dict:line(Location, Dict0), +    encode_op(line, [Index], Dict);  make_op({bif, Bif, {f,_}, [], Dest}, Dict) ->      %% BIFs without arguments cannot fail.      encode_op(bif0, [{extfunc, erlang, Bif, 0}, Dest], Dict); diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index c45874597a..432d1e7eea 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -36,13 +36,14 @@ function({function,Name,Arity,CLabel,Is0}, Lc0) ->  	%% Collect basic blocks and optimize them.  	Is2 = blockify(Is1), -	Is3 = move_allocates(Is2), -	Is4 = beam_utils:live_opt(Is3), -	Is5 = opt_blocks(Is4), -	Is6 = beam_utils:delete_live_annos(Is5), +	Is3 = embed_lines(Is2), +	Is4 = move_allocates(Is3), +	Is5 = beam_utils:live_opt(Is4), +	Is6 = opt_blocks(Is5), +	Is7 = beam_utils:delete_live_annos(Is6),  	%% Optimize bit syntax. -	{Is,Lc} = bsm_opt(Is6, Lc0), +	{Is,Lc} = bsm_opt(Is7, Lc0),  	%% Done.  	{{function,Name,Arity,CLabel,Is},Lc} @@ -148,6 +149,24 @@ collect(remove_message)      -> {set,[],[],remove_message};  collect({'catch',R,L})       -> {set,[R],[],{'catch',L}};  collect(_)                   -> error. +%% embed_lines([Instruction]) -> [Instruction] +%%  Combine blocks that would be split by line/1 instructions. +%%  Also move a line instruction before a block into the block, +%%  but leave the line/1 instruction after a block outside. + +embed_lines(Is) -> +    embed_lines(reverse(Is), []). + +embed_lines([{block,B2},{line,_}=Line,{block,B1}|T], Acc) -> +    B = {block,B1++[{set,[],[],Line}]++B2}, +    embed_lines([B|T], Acc); +embed_lines([{block,B1},{line,_}=Line|T], Acc) -> +    B = {block,[{set,[],[],Line}|B1]}, +    embed_lines([B|T], Acc); +embed_lines([I|Is], Acc) -> +    embed_lines(Is, [I|Acc]); +embed_lines([], Acc) -> Acc. +  opt_blocks([{block,Bl0}|Is]) ->      %% The live annotation at the beginning is not useful.      [{'%live',_}|Bl] = Bl0, @@ -225,10 +244,12 @@ opt([{set,[Dst],As,{bif,Bif,Fail}}=I1,   	RevBif -> [{set,[Dst],As,{bif,RevBif,Fail}}|opt(Is)]      end;  opt([{set,[X],[X],move}|Is]) -> opt(Is); -opt([{set,[D1],[{integer,Idx1},Reg],{bif,element,{f,0}}}=I1, +opt([{set,_,_,{line,_}}=Line1, +     {set,[D1],[{integer,Idx1},Reg],{bif,element,{f,0}}}=I1, +     {set,_,_,{line,_}}=Line2,       {set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is])    when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg -> -    opt([I2,I1|Is]); +    opt([Line2,I2,Line1,I1|Is]);  opt([{set,Ds0,Ss,Op}|Is0]) ->	      {Ds,Is} = opt_moves(Ds0, Is0),      [{set,Ds,Ss,Op}|opt(Is)]; diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl index 415864b8e9..1217f7f777 100644 --- a/lib/compiler/src/beam_bsm.erl +++ b/lib/compiler/src/beam_bsm.erl @@ -20,7 +20,7 @@  -module(beam_bsm).  -export([module/2,format_error/1]). --import(lists, [member/2,foldl/3,reverse/1,sort/1,all/2]). +-import(lists, [member/2,foldl/3,reverse/1,sort/1,all/2,dropwhile/2]).  %%%  %%% We optimize bit syntax matching where the tail end of a binary is @@ -376,6 +376,8 @@ btb_reaches_match_2([{func_info,_,_,Arity}=I|_], Regs0, D) ->  	[] -> D;  	_ -> {binary_used_in,I}      end; +btb_reaches_match_2([{line,_}|Is], Regs, D) -> +    btb_reaches_match_1(Is, Regs, D);  btb_reaches_match_2([I|_], Regs, _) ->      btb_error({btb_context_regs(Regs),I,not_handled}). @@ -580,7 +582,10 @@ btb_index(Fs) ->      btb_index_1(Fs, []).  btb_index_1([{function,_,_,Entry,Is0}|Fs], Acc0) -> -    [{label,_},{func_info,_,_,_},{label,Entry}|Is] = Is0, +    [{label,Entry}|Is] = +	dropwhile(fun({label,L}) when L =:= Entry -> false; +		     (_) -> true +		  end, Is0),      Acc = btb_index_2(Is, Entry, false, Acc0),      btb_index_1(Fs, Acc);  btb_index_1([], Acc) -> gb_trees:from_orddict(sort(Acc)). diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl index 64c93e11f7..a7994ab3b3 100644 --- a/lib/compiler/src/beam_clean.erl +++ b/lib/compiler/src/beam_clean.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. All Rights Reserved.  %%   %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -23,9 +23,9 @@  -export([module/2]).  -export([bs_clean_saves/1]).  -export([clean_labels/1]). --import(lists, [map/2,foldl/3,reverse/1]). +-import(lists, [map/2,foldl/3,reverse/1,filter/2]). -module({Mod,Exp,Attr,Fs0,_}, _Opt) -> +module({Mod,Exp,Attr,Fs0,_}, Opts) ->      Order = [Lbl || {function,_,_,Lbl,_} <- Fs0],      All = foldl(fun({function,_,_,Lbl,_}=Func,D) -> dict:store(Lbl, Func, D) end,  		dict:new(), Fs0), @@ -33,7 +33,8 @@ module({Mod,Exp,Attr,Fs0,_}, _Opt) ->      Used = find_all_used(WorkList, All, sets:from_list(WorkList)),      Fs1 = remove_unused(Order, Used, All),      {Fs2,Lc} = clean_labels(Fs1), -    Fs = bs_fix(Fs2), +    Fs3 = bs_fix(Fs2), +    Fs = maybe_remove_lines(Fs3, Opts),      {ok,{Mod,Exp,Attr,Fs,Lc}}.  %% Remove all bs_save2/2 instructions not referenced by a bs_restore2/2. @@ -375,3 +376,20 @@ bs_clean_saves_1([{bs_save2,_,{_,_}=SavePoint}=I|Is], Needed, Acc) ->  bs_clean_saves_1([I|Is], Needed, Acc) ->      bs_clean_saves_1(Is, Needed, [I|Acc]);  bs_clean_saves_1([], _, Acc) -> reverse(Acc). + +%%% +%%% Remove line instructions if requested. +%%% + +maybe_remove_lines(Fs, Opts) -> +    case proplists:get_bool(no_line_info, Opts) of +	false -> Fs; +	true -> remove_lines(Fs) +    end. + +remove_lines([{function,N,A,Lbl,Is0}|T]) -> +    Is = filter(fun({line,_}) -> false; +		   (_)  -> true +		end, Is0), +    [{function,N,A,Lbl,Is}|remove_lines(T)]; +remove_lines([]) -> []. diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl index 1365f3d20a..9f81a6ab43 100644 --- a/lib/compiler/src/beam_dead.erl +++ b/lib/compiler/src/beam_dead.erl @@ -144,9 +144,9 @@ function({function,Name,Arity,CLabel,Is0}, Lc0) ->  	%% Initialize label information with the code  	%% for the func_info label. Without it, a register  	%% may seem to be live when it is not. -	[{label,L},{func_info,_,_,_}=FI|_] = Is1, +	[{label,L}|FiIs] = Is1,  	D0 = beam_utils:empty_label_index(), -	D = beam_utils:index_label(L, [FI], D0), +	D = beam_utils:index_label(L, FiIs, D0),  	%% Optimize away dead code.  	{Is2,Lc} = forward(Is1, Lc0), @@ -185,6 +185,8 @@ split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc)      split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]);  split_block([{set,[R],[],{'catch',L}}|Is], Bl, Acc) ->      split_block(Is, [], [{'catch',R,L}|make_block(Bl, Acc)]); +split_block([{set,[],[],{line,_}=Line}|Is], Bl, Acc) -> +    split_block(Is, [], [Line|make_block(Bl, Acc)]);  split_block([I|Is], Bl, Acc) ->      split_block(Is, [I|Bl], Acc);  split_block([], Bl, Acc) -> make_block(Bl, Acc). @@ -406,7 +408,7 @@ backward([{test,Op,{f,To0},Live,Ops0,Dst}|Is], D, Acc) ->  	 end,      I = {test,Op,{f,To},Live,Ops0,Dst},      backward(Is, D, [I|Acc]); -backward([{kill,_}=I|Is], D, [Exit|_]=Acc) -> +backward([{kill,_}=I|Is], D, [{line,_},Exit|_]=Acc) ->      case beam_jump:is_exit_instruction(Exit) of  	false -> backward(Is, D, [I|Acc]);  	true -> backward(Is, D, Acc) @@ -471,7 +473,7 @@ shortcut_fail_label(To0, Reg, Val, D) ->  shortcut_boolean_label(To0, Reg, Bool0, D) when is_boolean(Bool0) ->      case beam_utils:code_at(To0, D) of -	[{bif,'not',_,[Reg],Reg},{jump,{f,To}}|_] -> +	[{line,_},{bif,'not',_,[Reg],Reg},{jump,{f,To}}|_] ->  	    Bool = not Bool0,  	    {shortcut_select_label(To, Reg, Bool, D),Bool};  	_ -> diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl index c50ed28aa9..ee76623976 100644 --- a/lib/compiler/src/beam_dict.erl +++ b/lib/compiler/src/beam_dict.erl @@ -22,9 +22,10 @@  -export([new/0,opcode/2,highest_opcode/1,  	 atom/2,local/4,export/4,import/4, -	 string/2,lambda/5,literal/2, +	 string/2,lambda/5,literal/2,line/2,fname/2,  	 atom_table/1,local_table/1,export_table/1,import_table/1, -	 string_table/1,lambda_table/1,literal_table/1]). +	 string_table/1,lambda_table/1,literal_table/1, +	 line_table/1]).  -type label() :: non_neg_integer(). @@ -36,6 +37,9 @@  	 strings = <<>>		    :: binary(),	%String pool  	 lambdas = [],				%[{...}]  	 literals = dict:new()	    :: dict(),	%Format: {Literal,Number} +	 fnames = gb_trees:empty()  :: gb_tree(),       %{Name,Index} +	 lines = gb_trees:empty()   :: gb_tree(),	%{{Fname,Line},Index} +	 num_lines = 0		    :: non_neg_integer(), %Number of line instructions  	 next_import = 0	    :: non_neg_integer(),  	 string_offset = 0	    :: non_neg_integer(),  	 next_literal = 0	    :: non_neg_integer(), @@ -152,6 +156,36 @@ literal(Lit, #asm{literals=Tab0,next_literal=NextIndex}=Dict) ->  	    {NextIndex,Dict#asm{literals=Tab,next_literal=NextIndex+1}}      end. +%% Returns the index for a line instruction (adding information +%% to the location information table). +-spec line(list(), bdict()) -> {non_neg_integer(), bdict()}. + +line([], #asm{num_lines=N}=Dict) -> +    %% No location available. Return the special pre-defined +    %% index 0. +    {0,Dict#asm{num_lines=N+1}}; +line([{location,Name,Line}], #asm{lines=Lines0,num_lines=N}=Dict0) -> +    {FnameIndex,Dict1} = fname(Name, Dict0), +    case gb_trees:lookup({FnameIndex,Line}, Lines0) of +	{value,Index} -> +	    {Index,Dict1#asm{num_lines=N+1}}; +	none -> +	    Index = gb_trees:size(Lines0) + 1, +	    Lines = gb_trees:insert({FnameIndex,Line}, Index, Lines0), +	    Dict = Dict1#asm{lines=Lines,num_lines=N+1}, +	    {Index,Dict} +    end. + +fname(Name, #asm{fnames=Fnames0}=Dict) -> +    case gb_trees:lookup(Name, Fnames0) of +	{value,Index} -> +	    {Index,Dict}; +	none -> +	    Index = gb_trees:size(Fnames0), +	    Fnames = gb_trees:insert(Name, Index, Fnames0), +	    {Index,Dict#asm{fnames=Fnames}} +    end. +  %% Returns the atom table.  %%    atom_table(Dict) -> {LastIndex,[Length,AtomString...]}  -spec atom_table(bdict()) -> {non_neg_integer(), [[non_neg_integer(),...]]}. @@ -219,6 +253,21 @@ literal_table(#asm{literals=Tab,next_literal=NumLiterals}) ->  my_term_to_binary(Term) ->      term_to_binary(Term, [{minor_version,1}]). +%% Return the line table. +-spec line_table(bdict()) -> +    {non_neg_integer(),				%Number of line instructions. +     non_neg_integer(),[string()], +     non_neg_integer(),[{non_neg_integer(),non_neg_integer()}]}. + +line_table(#asm{fnames=Fnames0,lines=Lines0,num_lines=NumLineInstrs}) -> +    NumFnames = gb_trees:size(Fnames0), +    Fnames1 = lists:keysort(2, gb_trees:to_list(Fnames0)), +    Fnames = [Name || {Name,_} <- Fnames1], +    NumLines = gb_trees:size(Lines0), +    Lines1 = lists:keysort(2, gb_trees:to_list(Lines0)), +    Lines = [L || {L,_} <- Lines1], +    {NumLineInstrs,NumFnames,Fnames,NumLines,Lines}. +  %% Search for binary string Str in the binary string pool Pool.  %%    old_string(Str, Pool) -> none | Index  -spec old_string(binary(), binary()) -> 'none' | pos_integer(). diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl index 017ca129b0..410233a0f7 100644 --- a/lib/compiler/src/beam_disasm.erl +++ b/lib/compiler/src/beam_disasm.erl @@ -296,6 +296,8 @@ get_function_chunks(Code) ->  labels_r([], R) -> {R, []};  labels_r([{label,_}=I|Is], R) ->      labels_r(Is, [I|R]); +labels_r([{line,_}=I|Is], R) -> +    labels_r(Is, [I|R]);  labels_r(Is, R) -> {R, Is}.  get_funs({[],[]}) -> []; @@ -335,20 +337,17 @@ local_labels(Funs) ->  				   local_labels_1(function__code(F), R)  			   end, [], Funs)). -%% The first clause below attempts to provide some (limited form of) -%% backwards compatibility; it is not needed for .beam files generated -%% by the R8 compiler.  The clause should one fine day be taken out. -local_labels_1([{label,_}|[{label,_}|_]=Code], R) -> -    local_labels_1(Code, R); -local_labels_1([{label,_},{func_info,{atom,M},{atom,F},A}|Code], R) -  when is_atom(M), is_atom(F) -> -    local_labels_2(Code, R, M, F, A); -local_labels_1(Code, _) -> -    ?exit({'local_labels: no label in code',Code}). +local_labels_1(Code0, R) -> +    Code1 = lists:dropwhile(fun({label,_}) -> true; +			       ({line,_}) -> true; +			       ({func_info,_,_,_}) -> false +			    end, Code0), +    [{func_info,{atom,M},{atom,F},A}|Code] = Code1, +    local_labels_2(Code, R, {M,F,A}). -local_labels_2([{label,[{u,L}]}|Code], R, M, F, A) -> -    local_labels_2(Code, [{L,{M,F,A}}|R], M, F, A); -local_labels_2(_, R, _, _, _) -> R. +local_labels_2([{label,[{u,L}]}|Code], R, MFA) -> +    local_labels_2(Code, [{L,MFA}|R], MFA); +local_labels_2(_, R, _) -> R.  %%-----------------------------------------------------------------------  %% Disassembles a single BEAM instruction; most instructions are handled @@ -1105,6 +1104,12 @@ resolve_inst({recv_set,[Lbl]},_,_,_) ->      {recv_set,Lbl};  %% +%% R15A. +%% +resolve_inst({line,[Index]},_,_,_) -> +    {line,resolve_arg(Index)}; + +%%  %% Catches instructions that are not yet handled.  %%  resolve_inst(X,_,_,_) -> ?exit({resolve_inst,X}). diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index 3cab55c4cb..537f8ca81b 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -169,7 +169,7 @@ share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->  	    share_1(Is, Dict0, [], [Lbl,{jump,{f,Label}}|Acc])      end;  share_1([{func_info,_,_,_}=I|Is], _, [], Acc) -> -    Is++[I|Acc]; +    reverse(Is, [I|Acc]);  share_1([I|Is], Dict, Seq, Acc) ->      case is_unreachable_after(I) of  	false -> @@ -206,25 +206,35 @@ is_label(_) -> false.  move(Is) ->      move_1(Is, [], []). -move_1([I|Is], End, Acc) -> +move_1([I|Is], End0, Acc0) ->      case is_exit_instruction(I) of -	false -> move_1(Is, End, [I|Acc]); -	true -> move_2(I, Is, End, Acc) +	false -> +	    move_1(Is, End0, [I|Acc0]); +	true -> +	    case extract_seq(Acc0, [I|End0]) of +		no -> +		    move_1(Is, End0, [I|Acc0]); +		{yes,End,Acc} -> +		    move_1(Is, End, Acc) +	    end      end; -move_1([], End, Acc) -> -    reverse(Acc, reverse(End)). - -move_2(Exit, Is, End, [{block,_},{label,_},{func_info,_,_,_}|_]=Acc) -> -    move_1(Is, End, [Exit|Acc]); -move_2(Exit, Is, End, [{block,_}=Blk,{label,_}=Lbl,Unreachable|More]) -> -    move_1([Unreachable|Is], [Exit,Blk,Lbl|End], More); -move_2(Exit, Is, End, [{bs_context_to_binary,_}=Bs,{label,_}=Lbl, -		       Unreachable|More]) -> -    move_1([Unreachable|Is], [Exit,Bs,Lbl|End], More); -move_2(Exit, Is, End, [{label,_}=Lbl,Unreachable|More]) -> -    move_1([Unreachable|Is], [Exit,Lbl|End], More); -move_2(Exit, Is, End, Acc) -> -    move_1(Is, End, [Exit|Acc]). +move_1([], End, Acc) -> reverse(Acc, End). + +extract_seq([{line,_}=Line|Is], Acc) -> +    extract_seq(Is, [Line|Acc]); +extract_seq([{block,_}=Bl|Is], Acc) -> +    extract_seq_1(Is, [Bl|Acc]); +extract_seq([{label,_}|_]=Is, Acc) -> +    extract_seq_1(Is, Acc); +extract_seq(_, _) -> no. + +extract_seq_1([{line,_}=Line|Is], Acc) -> +    extract_seq_1(Is, [Line|Acc]); +extract_seq_1([{label,_},{func_info,_,_,_}|_], _) -> +    no; +extract_seq_1([{label,_}=Lbl|Is], Acc) -> +    {yes,[Lbl|Acc],Is}; +extract_seq_1(_, _) -> no.  %%%  %%% (3) (4) (5) (6) Jump and unreachable code optimizations. @@ -454,6 +464,7 @@ is_label_used_in_2({set,_,_,Info}, Lbl) ->  	{put_tuple,_} -> false;  	{get_tuple_element,_} -> false;  	{set_tuple_element,_} -> false; +	{line,_} -> false;  	_ when is_atom(Info) -> false      end. @@ -487,6 +498,8 @@ rem_unused([], _, Acc) -> reverse(Acc).  initial_labels(Is) ->      initial_labels(Is, []). +initial_labels([{line,_}|Is], Acc) -> +    initial_labels(Is, Acc);  initial_labels([{label,Lbl}|Is], Acc) ->      initial_labels(Is, [Lbl|Acc]);  initial_labels([{func_info,_,_,_},{label,Lbl}|_], Acc) -> diff --git a/lib/compiler/src/beam_listing.erl b/lib/compiler/src/beam_listing.erl index be7b14c3dd..2941f6135c 100644 --- a/lib/compiler/src/beam_listing.erl +++ b/lib/compiler/src/beam_listing.erl @@ -61,7 +61,7 @@ print_op(Stream, Label) when element(1, Label) == label ->  print_op(Stream, Op) ->      io:format(Stream, "    ~p.\n", [Op]). -function(File, {function,Name,Arity,Args,Body,Vdb}) -> +function(File, {function,Name,Arity,Args,Body,Vdb,_Anno}) ->      io:nl(File),      io:format(File, "function ~p/~p.\n", [Name,Arity]),      io:format(File, " ~p.\n", [Args]), diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl index 9ed44ad5d7..c483d85a97 100644 --- a/lib/compiler/src/beam_receive.erl +++ b/lib/compiler/src/beam_receive.erl @@ -175,6 +175,8 @@ opt_update_regs({label,Lbl}, R, L) ->      end;  opt_update_regs({try_end,_}, R, L) ->      {R,L}; +opt_update_regs({line,_}, R, L) -> +    {R,L};  opt_update_regs(_I, _R, L) ->      %% Unrecognized instruction. Abort the search.      {regs_init(),L}. diff --git a/lib/compiler/src/beam_trim.erl b/lib/compiler/src/beam_trim.erl index 790aba0a9a..25e6ffbb73 100644 --- a/lib/compiler/src/beam_trim.erl +++ b/lib/compiler/src/beam_trim.erl @@ -222,7 +222,9 @@ remap([{call_last,Ar,Name,N}|Is], Map, Acc) ->      reverse(Acc, [I|Is]);  remap([{call_ext_last,Ar,Name,N}|Is], Map, Acc) ->      I = {call_ext_last,Ar,Name,Map({frame_size,N})}, -    reverse(Acc, [I|Is]). +    reverse(Acc, [I|Is]); +remap([{line,_}=I|Is], Map, Acc) -> +    remap(Is, Map, [I|Acc]).  remap_block([{set,Ds0,Ss0,Info}|Is], Map, Acc) ->      Ds = [Map(D) || D <- Ds0], @@ -230,14 +232,15 @@ remap_block([{set,Ds0,Ss0,Info}|Is], Map, Acc) ->      remap_block(Is, Map, [{set,Ds,Ss,Info}|Acc]);  remap_block([], _, Acc) -> reverse(Acc). -safe_labels([{label,L},{badmatch,{Tag,_}}|Is], Acc) when Tag =/= y -> +safe_labels([{label,L},{line,_},{badmatch,{Tag,_}}|Is], Acc) when Tag =/= y ->      safe_labels(Is, [L|Acc]); -safe_labels([{label,L},{case_end,{Tag,_}}|Is], Acc) when Tag =/= y -> +safe_labels([{label,L},{line,_},{case_end,{Tag,_}}|Is], Acc) when Tag =/= y ->      safe_labels(Is, [L|Acc]); -safe_labels([{label,L},if_end|Is], Acc) -> +safe_labels([{label,L},{line,_},if_end|Is], Acc) ->      safe_labels(Is, [L|Acc]);  safe_labels([{label,L},  	     {block,[{set,[{x,0}],[{Tag,_}],move}]}, +	     {line,_},  	     {call_ext,1,{extfunc,erlang,error,1}}|Is], Acc) when Tag =/= y ->      safe_labels(Is, [L|Acc]);  safe_labels([_|Is], Acc) -> @@ -321,6 +324,8 @@ frame_size([{make_fun2,_,_,_,_}|Is], Safe) ->  frame_size([{deallocate,N}|_], _) -> N;  frame_size([{call_last,_,_,N}|_], _) -> N;  frame_size([{call_ext_last,_,_,N}|_], _) -> N; +frame_size([{line,_}|Is], Safe) -> +    frame_size(Is, Safe);  frame_size([_|_], _) -> throw(not_possible).  frame_size_branch(0, Is, Safe) -> diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index f83f73b224..7fdb8d072a 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -400,6 +400,7 @@ update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) ->  update({call,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);  update({call_ext,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);  update({make_fun2,_,_,_,_}, Ts) -> tdb_kill_xregs(Ts); +update({line,_}, Ts) -> Ts;  %% The instruction is unknown.  Kill all information.  update(_I, _Ts) -> tdb_new(). diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 45cdf8a659..f281ad5eac 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -26,7 +26,7 @@  	 code_at/2,bif_to_test/3,is_pure_test/1,  	 live_opt/1,delete_live_annos/1,combine_heap_needs/2]). --import(lists, [member/2,sort/1,reverse/1]). +-import(lists, [member/2,sort/1,reverse/1,splitwith/2]).  -record(live,  	{bl,					%Block check fun. @@ -195,10 +195,14 @@ is_pure_test({test,Op,_,Ops}) ->  %%  Also insert {'%live',Live} annotations at the beginning  %%  and end of each block.  %% -live_opt([{label,Fail}=I1, -	  {func_info,_,_,Live}=I2|Is]) -> +live_opt(Is0) -> +    {[{label,Fail}|_]=Bef,[Fi|Is]} = +	splitwith(fun({func_info,_,_,_}) -> false; +		     (_) -> true +		  end, Is0), +    {func_info,_,_,Live} = Fi,      D = gb_trees:insert(Fail, live_call(Live), gb_trees:empty()), -    [I1,I2|live_opt(reverse(Is), 0, D, [])]. +    Bef ++ [Fi|live_opt(reverse(Is), 0, D, [])].  %% delete_live_annos([Instruction]) -> [Instruction]. @@ -499,6 +503,8 @@ check_liveness(R, [{loop_rec,{f,_},{x,0}}|_], St) ->      end;  check_liveness(R, [{loop_rec_end,{f,Fail}}|_], St) ->      check_liveness_at(R, Fail, St); +check_liveness(R, [{line,_}|Is], St) -> +    check_liveness(R, Is, St);  check_liveness(_R, Is, St) when is_list(Is) ->  %%     case Is of  %% 	[I|_] -> @@ -799,6 +805,8 @@ live_opt([{wait,_}=I|Is], Regs, D, Acc) ->      live_opt(Is, Regs, D, [I|Acc]);  live_opt([{wait_timeout,_,{Tag,_}}=I|Is], Regs, D, Acc) when Tag =/= x ->      live_opt(Is, Regs, D, [I|Acc]); +live_opt([{line,_}=I|Is], Regs, D, Acc) -> +    live_opt(Is, Regs, D, [I|Acc]);  %% The following instructions can occur if the "compilation" has been  %% started from a .S file using the 'asm' option. diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index fb267b35b6..fe3b1680d9 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -166,12 +166,17 @@ validate(Module, Fs) ->      Ft = index_bs_start_match(Fs, []),      validate_0(Module, Fs, Ft). -index_bs_start_match([{function,_,_,Entry,Code}|Fs], Acc0) -> +index_bs_start_match([{function,_,_,Entry,Code0}|Fs], Acc0) -> +    Code = dropwhile(fun({label,L}) when L =:= Entry -> false; +			(_) -> true +		     end, Code0),      case Code of -	[_,_,{label,Entry}|Is] -> +	[{label,Entry}|Is] ->  	    Acc = index_bs_start_match_1(Is, Entry, Acc0),  	    index_bs_start_match(Fs, Acc);  	_ -> +	    %% Something serious is wrong. Ignore it for now. +	    %% It will be detected and diagnosed later.  	    index_bs_start_match(Fs, Acc0)      end;  index_bs_start_match([], Acc) -> @@ -292,6 +297,8 @@ labels(Is) ->  labels_1([{label,L}|Is], R) ->      labels_1(Is, [L|R]); +labels_1([{line,_}|Is], R) -> +    labels_1(Is, R);  labels_1(Is, R) ->      {lists:reverse(R),Is}. @@ -433,6 +440,8 @@ valfun_1(remove_message, Vst) ->      Vst;  valfun_1({'%',_}, Vst) ->      Vst; +valfun_1({line,_}, Vst) -> +    Vst;  %% Exception generating calls  valfun_1({call_ext,Live,Func}=I, Vst) ->      case return_type(Func, Vst) of @@ -870,6 +879,8 @@ val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=false}}) ->      error(illegal_context_for_set_tuple_element);  val_dsetel({set_tuple_element,_,_,_}, #vst{current=#st{setelem=true}}=Vst) ->      Vst; +val_dsetel({line,_}, Vst) -> +    Vst;  val_dsetel(_, #vst{current=#st{setelem=true}=St}=Vst) ->      Vst#vst{current=St#st{setelem=false}};  val_dsetel(_, Vst) -> Vst. diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index e46c667e47..15849957e7 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -172,9 +172,9 @@ expand_opt(report, Os) ->  expand_opt(return, Os) ->      [return_errors,return_warnings|Os];  expand_opt(r12, Os) -> -    [no_recv_opt|Os]; +    [no_recv_opt,no_line_info|Os];  expand_opt(r13, Os) -> -    [no_recv_opt|Os]; +    [no_recv_opt,no_line_info|Os];  expand_opt({debug_info_key,_}=O, Os) ->      [encrypt_debug_info,O|Os];  expand_opt(no_float_opt, Os) -> @@ -1438,6 +1438,8 @@ iofile(File) when is_atom(File) ->  iofile(File) ->      {filename:dirname(File), filename:basename(File, ".erl")}. +erlfile(".", Base, Suffix) -> +    Base ++ Suffix;  erlfile(Dir, Base, Suffix) ->      filename:join(Dir, Base ++ Suffix). diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl index f8128702dd..2514c06360 100644 --- a/lib/compiler/src/erl_bifs.erl +++ b/lib/compiler/src/erl_bifs.erl @@ -72,7 +72,6 @@ is_pure(erlang, binary_to_list, 1) -> true;  is_pure(erlang, binary_to_list, 3) -> true;  is_pure(erlang, bit_size, 1) -> true;  is_pure(erlang, byte_size, 1) -> true; -is_pure(erlang, concat_binary, 1) -> true;  is_pure(erlang, element, 2) -> true;  is_pure(erlang, float, 1) -> true;  is_pure(erlang, float_to_list, 1) -> true; diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab index 63527bda8f..39c1e8297f 100644 --- a/lib/compiler/src/genop.tab +++ b/lib/compiler/src/genop.tab @@ -280,3 +280,7 @@ BEAM_FORMAT_NUMBER=0  150: recv_mark/1  151: recv_set/1  152: gc_bif3/7 + +# R15A + +153: line/1 diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 55e3c58d2a..e7dae67085 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -79,9 +79,10 @@ module({Mod,Exp,Attr,Forms}, Options) ->  functions(Forms, AtomMod) ->      mapfoldl(fun (F, St) -> function(F, AtomMod, St) end, #cg{lcount=1}, Forms). -function({function,Name,Arity,Asm0,Vb,Vdb}, AtomMod, St0) -> +function({function,Name,Arity,Asm0,Vb,Vdb,Anno}, AtomMod, St0) ->      try -	{Asm,EntryLabel,St} = cg_fun(Vb, Asm0, Vdb, AtomMod, {Name,Arity}, St0), +	{Asm,EntryLabel,St} = cg_fun(Vb, Asm0, Vdb, AtomMod, +				     {Name,Arity}, Anno, St0),  	Func = {function,Name,Arity,EntryLabel,Asm},  	{Func,St}      catch @@ -93,7 +94,7 @@ function({function,Name,Arity,Asm0,Vb,Vdb}, AtomMod, St0) ->  %% cg_fun([Lkexpr], [HeadVar], Vdb, State) -> {[Ainstr],State} -cg_fun(Les, Hvs, Vdb, AtomMod, NameArity, St0) -> +cg_fun(Les, Hvs, Vdb, AtomMod, NameArity, Anno, St0) ->      {Fi,St1} = new_label(St0),			%FuncInfo label      {Fl,St2} = local_func_label(NameArity, St1), @@ -129,7 +130,7 @@ cg_fun(Les, Hvs, Vdb, AtomMod, NameArity, St0) ->  				 ultimate_failure=UltimateMatchFail,  				 is_top_block=true}),      {Name,Arity} = NameArity, -    Asm = [{label,Fi},{func_info,AtomMod,{atom,Name},Arity}, +    Asm = [{label,Fi},line(Anno),{func_info,AtomMod,{atom,Name},Arity},  	   {label,Fl}|B++[{label,UltimateMatchFail},if_end]],      {Asm,Fl,St}. @@ -307,23 +308,23 @@ match_fail_cg({badmatch,Term}, Le, Vdb, Bef, St) ->      R = cg_reg_arg(Term, Bef),      Int0 = clear_dead(Bef, Le#l.i, Vdb),      {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb), -    {Sis ++ [{badmatch,R}], +    {Sis ++ [line(Le),{badmatch,R}],       Int#sr{reg=clear_regs(Int0#sr.reg)},St};  match_fail_cg({case_clause,Reason}, Le, Vdb, Bef, St) ->      R = cg_reg_arg(Reason, Bef),      Int0 = clear_dead(Bef, Le#l.i, Vdb),      {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb), -    {Sis++[{case_end,R}], +    {Sis++[line(Le),{case_end,R}],       Int#sr{reg=clear_regs(Bef#sr.reg)},St};  match_fail_cg(if_clause, Le, Vdb, Bef, St) ->      Int0 = clear_dead(Bef, Le#l.i, Vdb),      {Sis,Int1} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb), -    {Sis++[if_end],Int1#sr{reg=clear_regs(Int1#sr.reg)},St}; +    {Sis++[line(Le),if_end],Int1#sr{reg=clear_regs(Int1#sr.reg)},St};  match_fail_cg({try_clause,Reason}, Le, Vdb, Bef, St) ->      R = cg_reg_arg(Reason, Bef),      Int0 = clear_dead(Bef, Le#l.i, Vdb),      {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb), -    {Sis ++ [{try_case_end,R}], +    {Sis ++ [line(Le),{try_case_end,R}],       Int#sr{reg=clear_regs(Int0#sr.reg)},St}.  %% bsm_rename_ctx([Clause], Var) -> [Clause] @@ -1047,7 +1048,7 @@ call_cg({var,_V} = Var, As, Rs, Le, Vdb, Bef, St0) ->      %% Build complete code and final stack/register state.      Arity = length(As),      {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)), -    {Sis ++ Frees ++ [{call_fun,Arity}],Aft, +    {Sis ++ Frees ++ [line(Le),{call_fun,Arity}],Aft,       need_stack_frame(St0)};  call_cg({remote,Mod,Name}, As, Rs, Le, Vdb, Bef, St0)    when element(1, Mod) =:= var; @@ -1057,11 +1058,10 @@ call_cg({remote,Mod,Name}, As, Rs, Le, Vdb, Bef, St0)      Reg = load_vars(Rs, clear_regs(Int#sr.reg)),      %% Build complete code and final stack/register state.      Arity = length(As), -    Call = {apply,Arity},      St = need_stack_frame(St0),      %%{Call,St1} = build_call(Func, Arity, St0),      {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)), -    {Sis ++ Frees ++ [Call],Aft,St}; +    {Sis ++ Frees ++ [line(Le),{apply,Arity}],Aft,St};  call_cg(Func, As, Rs, Le, Vdb, Bef, St0) ->      case St0 of  	#cg{bfail=Fail} when Fail =/= 0 -> @@ -1091,7 +1091,7 @@ call_cg(Func, As, Rs, Le, Vdb, Bef, St0) ->  	    Arity = length(As),  	    {Call,St1} = build_call(Func, Arity, St0),  	    {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)), -	    {Sis ++ Frees ++ Call,Aft,St1} +	    {Sis ++ Frees ++ [line(Le)|Call],Aft,St1}      end.  build_call({remote,{atom,erlang},{atom,'!'}}, 2, St0) -> @@ -1118,7 +1118,7 @@ enter_cg({var,_V} = Var, As, Le, Vdb, Bef, St0) ->      {Sis,Int} = cg_setup_call(As++[Var], Bef, Le#l.i, Vdb),      %% Build complete code and final stack/register state.      Arity = length(As), -    {Sis ++ [{call_fun,Arity},return], +    {Sis ++ [line(Le),{call_fun,Arity},return],       clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),       need_stack_frame(St0)};  enter_cg({remote,Mod,Name}, As, Le, Vdb, Bef, St0) @@ -1127,9 +1127,8 @@ enter_cg({remote,Mod,Name}, As, Le, Vdb, Bef, St0)      {Sis,Int} = cg_setup_call(As++[Mod,Name], Bef, Le#l.i, Vdb),      %% Build complete code and final stack/register state.      Arity = length(As), -    Call = {apply_only,Arity},      St = need_stack_frame(St0), -    {Sis ++ [Call], +    {Sis ++ [line(Le),{apply_only,Arity}],       clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),       St};  enter_cg(Func, As, Le, Vdb, Bef, St0) -> @@ -1137,7 +1136,8 @@ enter_cg(Func, As, Le, Vdb, Bef, St0) ->      %% Build complete code and final stack/register state.      Arity = length(As),      {Call,St1} = build_enter(Func, Arity, St0), -    {Sis ++ Call, +    Line = enter_line(Func, Arity, Le), +    {Sis ++ Line ++ Call,       clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),       St1}. @@ -1153,6 +1153,23 @@ build_enter(Name, Arity, St0) when is_atom(Name) ->      {Lbl,St1} = local_func_label(Name, Arity, St0),      {[{call_only,Arity,{f,Lbl}}],St1}. +enter_line({remote,{atom,Mod},{atom,Name}}, Arity, Le) -> +    case erl_bifs:is_safe(Mod, Name, Arity) of +	false -> +	    %% Tail-recursive call, possibly to a BIF. +	    %% We'll need a line instruction in case the +	    %% BIF call fails. +	    [line(Le)]; +	true -> +	    %% Call to a safe BIF. Since it cannot fail, +	    %% we don't need any line instruction here. +	    [] +    end; +enter_line(_, _, _) -> +    %% Tail-recursive call to a local function. A line +    %% instruction will not be useful. +    []. +  %% local_func_label(Name, Arity, State) -> {Label,State'}  %% local_func_label({Name,Arity}, State) -> {Label,State'}  %%  Get the function entry label for a local function. @@ -1226,9 +1243,10 @@ bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->      %%   Currently, we are somewhat pessimistic in      %% that we save any variable that will be live after this BIF call. +    MayFail = not erl_bifs:is_safe(erlang, Bif, length(As)),      {Sis,Int0} = case St0#cg.in_catch andalso  		     St0#cg.bfail =:= 0 andalso -		     not erl_bifs:is_safe(erlang, Bif, length(As)) of +		     MayFail of  		     true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);  		     false -> {[],Bef}  		 end, @@ -1237,7 +1255,14 @@ bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->      Int = Int1#sr{reg=Reg},      Dst = fetch_reg(V, Reg),      BifFail = {f,St0#cg.bfail}, -    {Sis++[{bif,Bif,BifFail,Ars,Dst}], +    %% We need a line instructions for BIFs that may fail in a body. +    Line = case BifFail of +	       {f,0} when MayFail -> +		   [line(Le)]; +	       _ -> +		   [] +	   end, +    {Sis++Line++[{bif,Bif,BifFail,Ars,Dst}],       clear_dead(Int, Le#l.i, Vdb), St0}. @@ -1266,7 +1291,11 @@ gc_bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->      Int = Int1#sr{reg=Reg},      Dst = fetch_reg(V, Reg),      BifFail = {f,St0#cg.bfail}, -    {Sis++[{gc_bif,Bif,BifFail,max_reg(Bef#sr.reg),Ars,Dst}], +    Line = case BifFail of +	       {f,0} -> [line(Le)]; +	       {f,_} -> [] +	   end, +    {Sis++Line++[{gc_bif,Bif,BifFail,max_reg(Bef#sr.reg),Ars,Dst}],       clear_dead(Int, Le#l.i, Vdb), St0}.  %% recv_loop_cg(TimeOut, ReceiveVar, ReceiveMatch, TimeOutExprs, @@ -1284,7 +1313,7 @@ recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, Vdb, Bef, St0) ->      {Wis,Taft,St6} = cg_recv_wait(Te, Tes, Le#l.i, Int1, St5),      Int2 = sr_merge(Raft, Taft),		%Merge stack/registers      Reg = load_vars(Rs, Int2#sr.reg), -    {Sis ++ Ris ++ [{label,Tl}] ++ Wis ++ [{label,Bl}], +    {Sis ++ [line(Le)] ++ Ris ++ [{label,Tl}] ++ Wis ++ [{label,Bl}],       clear_dead(Int2#sr{reg=Reg}, Le#l.i, Vdb),       St6#cg{break=St0#cg.break,recv=St0#cg.recv}}. @@ -1463,12 +1492,13 @@ cg_binary([{bs_put_binary,Fail,{atom,all},U,_Flags,Src}|PutCode],  		 {bs_append,Fail,Target,0,MaxRegs,U,Src,BinFlags,Target}  	 end] ++ PutCode,      cg_bin_opt(Code); -cg_binary(PutCode, Target, Temp, Fail, MaxRegs, _Anno) -> +cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Anno) -> +    Line = line(Anno),      Live = cg_live(Target, MaxRegs),      {InitOp,SzCode} = cg_binary_size(PutCode, Target, Temp, Fail, Live), -    Code = SzCode ++ [{InitOp,Fail,Target,0,MaxRegs, -		       {field_flags,[]},Target}|PutCode], +    Code = [Line|SzCode] ++ [{InitOp,Fail,Target,0,MaxRegs, +			      {field_flags,[]},Target}|PutCode],      cg_bin_opt(Code).  cg_live({x,X}, MaxRegs) when X =:= MaxRegs -> MaxRegs+1; @@ -2052,6 +2082,38 @@ drop_catch(Tag, [Other|Stk]) -> [Other|drop_catch(Tag, Stk)].  new_label(#cg{lcount=Next}=St) ->      {Next,St#cg{lcount=Next+1}}. +%% line(Le) -> {line,[] | {location,File,Line}} +%%  Create a line instruction, containing information about +%%  the current filename and line number. A line information +%%  instruction should be placed before any operation that could +%%  cause an exception. + +line(#l{a=Anno}) -> +    line(Anno); +line([Line,{file,Name}]) when is_integer(Line) -> +    line_1(Name, Line); +line([_|_]=A) -> +    {Name,Line} = find_loc(A, no_file, 0), +    line_1(Name, Line); +line([]) -> +    {line,[]}. + +line_1(no_file, _) -> +    {line,[]}; +line_1(_, 0) -> +    %% Missing line number or line number 0. +    {line,[]}; +line_1(Name, Line) -> +    {line,[{location,Name,abs(Line)}]}. + +find_loc([Line|T], File, _) when is_integer(Line) -> +    find_loc(T, File, Line); +find_loc([{file,File}|T], _, Line) -> +    find_loc(T, File, Line); +find_loc([_|T], File, Line) -> +    find_loc(T, File, Line); +find_loc([], File, Line) -> {File,Line}. +  flatmapfoldl(F, Accu0, [Hd|Tail]) ->      {R,Accu1} = F(Hd, Accu0),      {Rs,Accu2} = flatmapfoldl(F, Accu1, Tail), diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 87bb5bec25..6f3590b156 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -180,7 +180,7 @@ body(Cs0, Name, Arity, St0) ->      {Args,St1} = new_vars(Anno, Arity, St0),      {Cs1,St2} = clauses(Cs0, St1),      {Ps,St3} = new_vars(Arity, St2),    %Need new variables here -    Fc = function_clause(Ps, {Name,Arity}), +    Fc = function_clause(Ps, Anno, {Name,Arity}),      {#ifun{anno=#a{anno=Anno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3}.  %% clause(Clause, State) -> {Cclause,State} | noclause. @@ -507,15 +507,15 @@ expr({block,_,Es0}, St0) ->      {E1,Es1 ++ Eps,St2};  expr({'if',L,Cs0}, St0) ->      {Cs1,St1} = clauses(Cs0, St0), -    Fc = fail_clause([], #c_literal{val=if_clause}),      Lanno = lineno_anno(L, St1), +    Fc = fail_clause([], Lanno, #c_literal{val=if_clause}),      {#icase{anno=#a{anno=Lanno},args=[],clauses=Cs1,fc=Fc},[],St1};  expr({'case',L,E0,Cs0}, St0) ->      {E1,Eps,St1} = novars(E0, St0),      {Cs1,St2} = clauses(Cs0, St1),      {Fpat,St3} = new_var(St2), -    Fc = fail_clause([Fpat], c_tuple([#c_literal{val=case_clause},Fpat])), -    Lanno = lineno_anno(L, St3), +    Lanno = lineno_anno(L, St2), +    Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=case_clause},Fpat])),      {#icase{anno=#a{anno=Lanno},args=[E1],clauses=Cs1,fc=Fc},Eps,St3};  expr({'receive',L,Cs0}, St0) ->      {Cs1,St1} = clauses(Cs0, St0), @@ -541,9 +541,10 @@ expr({'try',L,Es0,Cs0,Ecs,[]}, St0) ->      {V,St2} = new_var(St1),		%This name should be arbitrary      {Cs1,St3} = clauses(Cs0, St2),      {Fpat,St4} = new_var(St3), -    Fc = fail_clause([Fpat], c_tuple([#c_literal{val=try_clause},Fpat])), +    Lanno = lineno_anno(L, St4), +    Fc = fail_clause([Fpat], Lanno, +		     c_tuple([#c_literal{val=try_clause},Fpat])),      {Evs,Hs,St5} = try_exception(Ecs, St4), -    Lanno = lineno_anno(L, St1),      {#itry{anno=#a{anno=lineno_anno(L, St5)},args=Es1,  	   vars=[V],body=[#icase{anno=#a{anno=Lanno},args=[V],clauses=Cs1,fc=Fc}],  	   evars=Evs,handler=Hs}, @@ -607,8 +608,8 @@ expr({match,L,P0,E0}, St0) ->  		 Thrown  	 end,      {Fpat,St4} = new_var(St3), -    Fc = fail_clause([Fpat], c_tuple([#c_literal{val=badmatch},Fpat])),      Lanno = lineno_anno(L, St4), +    Fc = fail_clause([Fpat], Lanno, c_tuple([#c_literal{val=badmatch},Fpat])),      case P2 of  	nomatch ->  	    St = add_warning(L, nomatch, St4), @@ -828,8 +829,9 @@ fun_tq({_,_,Name}=Id, Cs0, L, St0) ->      {Cs1,St1} = clauses(Cs0, St0),      {Args,St2} = new_vars(Arity, St1),      {Ps,St3} = new_vars(Arity, St2),		%Need new variables here -    Fc = function_clause(Ps, {Name,Arity}), -    Fun = #ifun{anno=#a{anno=lineno_anno(L, St3)}, +    Anno = lineno_anno(L, St3), +    Fc = function_clause(Ps, Anno, {Name,Arity}), +    Fun = #ifun{anno=#a{anno=Anno},  		id=[{id,Id}],				%We KNOW!  		vars=Args,clauses=Cs1,fc=Fc},      {Fun,[],St3}. @@ -929,7 +931,7 @@ lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) ->       [],St};  lc_tq(Line, E, [Fil0|Qs0], Mc, St0) ->      %% Special case sequences guard tests. -    LA = lineno_anno(Line, St0), +    LA = lineno_anno(element(2, Fil0), St0),      LAnno = #a{anno=LA},      case is_guard_test(Fil0) of  	true -> @@ -945,7 +947,8 @@ lc_tq(Line, E, [Fil0|Qs0], Mc, St0) ->  	false ->  	    {Lc,Lps,St1} = lc_tq(Line, E, Qs0, Mc, St0),  	    {Fpat,St2} = new_var(St1), -	    Fc = fail_clause([Fpat], c_tuple([#c_literal{val=case_clause},Fpat])), +	    Fc = fail_clause([Fpat], LA, +			     c_tuple([#c_literal{val=case_clause},Fpat])),  	    %% Do a novars little optimisation here.  	    {Filc,Fps,St3} = novars(Fil0, St2),  	    {#icase{anno=LAnno, @@ -1072,7 +1075,7 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) ->       [],St};  bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) ->      %% Special case sequences guard tests. -    LA = lineno_anno(Line, St0), +    LA = lineno_anno(element(2, Fil0), St0),      LAnno = #a{anno=LA},      case is_guard_test(Fil0) of  	true -> @@ -1089,7 +1092,8 @@ bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) ->  	false ->  	    {Bc,Bps,St1} = bc_tq1(Line, E, Qs0, AccVar, St0),  	    {Fpat,St2} = new_var(St1), -	    Fc = fail_clause([Fpat], c_tuple([#c_literal{val=case_clause},Fpat])), +	    Fc = fail_clause([Fpat], LA, +			     c_tuple([#c_literal{val=case_clause},Fpat])),  	    %% Do a novars little optimisation here.  	    {Filc,Fps,St} = novars(Fil0, St2),  	    {#icase{anno=LAnno, @@ -1562,17 +1566,11 @@ new_vars_1(N, Anno, St0, Vs) when N > 0 ->      new_vars_1(N-1, Anno, St1, [V|Vs]);  new_vars_1(0, _, St, Vs) -> {Vs,St}. -function_clause(Ps, Name) -> -    function_clause(Ps, [], Name). -  function_clause(Ps, LineAnno, Name) -> -    FcAnno = [{function_name,Name}], +    FcAnno = [{function_name,Name}|LineAnno],      fail_clause(Ps, FcAnno,  		ann_c_tuple(LineAnno, [#c_literal{val=function_clause}|Ps])). -fail_clause(Pats, Arg) -> -    fail_clause(Pats, [], Arg). -  fail_clause(Pats, Anno, Arg) ->      #iclause{anno=#a{anno=[compiler_generated]},  	     pats=Pats,guard=[], diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 3b33a08cf7..4e06b464a4 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -247,7 +247,7 @@ expr(#c_var{anno=A,name={_Name,Arity}}=Fname, Sub, St) ->      %% instead of one for each occurrence as done now.      Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||  	     V <- integers(1, Arity)], -    Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{op=Fname,args=Vs}}, +    Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{anno=A,op=Fname,args=Vs}},      expr(Fun, Sub, St);  expr(#c_var{anno=A,name=V}, Sub, St) ->      {#k_var{anno=A,name=get_vsub(V, Sub)},[],St}; @@ -291,7 +291,7 @@ expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->  	    Erl = #c_literal{val=erlang},  	    Name = #c_literal{val=error},  	    Args = [#c_literal{val=badarg}], -	    Error = #c_call{module=Erl,name=Name,args=Args}, +	    Error = #c_call{anno=A,module=Erl,name=Name,args=Args},  	    expr(Error, Sub, St0)      end;  expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, #kern{ff=OldFF,func=Func}=St0) -> @@ -1167,9 +1167,7 @@ select_bin_int_1(_, _, _, _) -> throw(not_possible).  select_assert_match_possible(Sz, Val, Fs) ->      EmptyBindings = erl_eval:new_bindings(), -    MatchFun = fun({integer,_,_}, NewV, Bs) when NewV =:= Val -> -		       {match,Bs} -	       end, +    MatchFun = match_fun(Val),      EvalFun = fun({integer,_,S}, B) -> {value,S,B} end,      Expr = [{bin_element,0,{integer,0,Val},{integer,0,Sz},[{unit,1}|Fs]}],      {value,Bin,EmptyBindings} = eval_bits:expr_grp(Expr, EmptyBindings, EvalFun), @@ -1184,6 +1182,11 @@ select_assert_match_possible(Sz, Val, Fs) ->  	    throw(not_possible)      end. +match_fun(Val) -> +    fun(match, {{integer,_,_},NewV,Bs}) when NewV =:= Val -> +	    {match,Bs} +    end. +  select_utf8(Val0) ->      try  	Bin = <<Val0/utf8>>, diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index a7a4d4dc91..a1d92af9f8 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -65,7 +65,7 @@ functions([], Acc) -> reverse(Acc).  %% function(Kfunc) -> Func. -function(#k_fdef{func=F,arity=Ar,vars=Vs,body=Kb}) -> +function(#k_fdef{anno=#k{a=Anno},func=F,arity=Ar,vars=Vs,body=Kb}) ->      try  	As = var_list(Vs),  	Vdb0 = foldl(fun ({var,N}, Vdb) -> new_var(N, 0, Vdb) end, [], As), @@ -80,7 +80,7 @@ function(#k_fdef{func=F,arity=Ar,vars=Vs,body=Kb}) ->  	put(guard_refc, 0),  	{B1,_,Vdb1} = body(B0, 1, Vdb0),  	erase(guard_refc), -	{function,F,Ar,As,B1,Vdb1} +	{function,F,Ar,As,B1,Vdb1,Anno}      catch  	Class:Error ->  	    Stack = erlang:get_stacktrace(), diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index 6a795f6634..f8c71a0257 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -1028,8 +1028,8 @@ haystack_2(Haystack) ->  fc({'EXIT',{function_clause,_}}) -> ok;  fc({'EXIT',{{case_clause,_},_}}) when ?MODULE =:= bs_match_inline_SUITE -> ok. -fc(Name, Args, {'EXIT',{function_clause,[{?MODULE,Name,Args}|_]}}) -> ok; -fc(Name, Args, {'EXIT',{function_clause,[{?MODULE,Name,Arity}|_]}}) +fc(Name, Args, {'EXIT',{function_clause,[{?MODULE,Name,Args,_}|_]}}) -> ok; +fc(Name, Args, {'EXIT',{function_clause,[{?MODULE,Name,Arity,_}|_]}})    when length(Args) =:= Arity ->      true = test_server:is_native(?MODULE);  fc(_, Args, {'EXIT',{{case_clause,ActualArgs},_}}) diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index b3e5376ffd..8c6a623dfb 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -82,6 +82,7 @@ file_1(Config) when is_list(Config) ->      ?line {ok,simple} = compile:file(Simple, [native,report]), %Smoke test.      ?line {ok,simple} = compile:file(Target, [native,from_beam]), %Smoke test.      ?line {ok,simple} = compile:file(Simple, [debug_info]), +    ?line {ok,simple} = compile:file(Simple, [no_line_info]), %Coverage      ?line ok = file:set_cwd(Cwd),      ?line true = exists(Target),      ?line passed = run(Target, test, []), diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index 0e69efba6b..40711783ed 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -32,7 +32,8 @@  	 t_is_boolean/1,is_function_2/1,  	 tricky/1,rel_ops/1,literal_type_tests/1,  	 basic_andalso_orelse/1,traverse_dcd/1, -	 check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1]). +	 check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1, +	 bad_constants/1]).  suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -44,7 +45,8 @@ all() ->       more_xor_guards, build_in_guard, old_guard_tests, gbif,       t_is_boolean, is_function_2, tricky, rel_ops,       literal_type_tests, basic_andalso_orelse, traverse_dcd, -     check_qlc_hrl, andalso_semi, t_tuple_size, binary_part]. +     check_qlc_hrl, andalso_semi, t_tuple_size, binary_part, +     bad_constants].  groups() ->       []. @@ -1517,8 +1519,27 @@ bptest(B,A,C)  when erlang:binary_part(B,{A,C}) =:= <<3,3>> ->  bptest(_,_,_) ->      error. - - +-define(FAILING(C), +	if +	    C -> ?t:fail(should_fail); +	    true -> ok +	end, +	if +	    true, C -> ?t:fail(should_fail); +	    true -> ok +	end). + +bad_constants(Config) when is_list(Config) -> +    ?line ?FAILING(false), +    ?line ?FAILING([]), +    ?line ?FAILING([a]), +    ?line ?FAILING([Config]), +    ?line ?FAILING({a,b}), +    ?line ?FAILING({a,Config}), +    ?line ?FAILING(<<1>>), +    ?line ?FAILING(42), +    ?line ?FAILING(3.14), +    ok.  %% Call this function to turn off constant propagation.  id(I) -> I. diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl index af2b8ec92a..086fba2649 100644 --- a/lib/compiler/test/inline_SUITE.erl +++ b/lib/compiler/test/inline_SUITE.erl @@ -263,7 +263,8 @@ my_apply(M, F, A, Init) ->  really_inlined(Config) when is_list(Config) ->      %% Make sure that badarg/2 really gets inlined. -    {'EXIT',{badarg,[{?MODULE,fail_me_now,[]}|_]}} = (catch fail_me_now()), +    {'EXIT',{badarg,[{?MODULE,fail_me_now,[],_}|_]}} = +	(catch fail_me_now()),      ok.  fail_me_now() -> diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl index c8908858ba..f5948504b3 100644 --- a/lib/compiler/test/lc_SUITE.erl +++ b/lib/compiler/test/lc_SUITE.erl @@ -179,8 +179,8 @@ empty_generator(Config) when is_list(Config) ->  id(I) -> I. -fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args}|_]}}) -> ok; -fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Arity}|_]}}) +fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args,_}|_]}}) -> ok; +fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Arity,_}|_]}})    when length(Args) =:= Arity ->      true = test_server:is_native(?MODULE);  fc(Args, {'EXIT',{{case_clause,ActualArgs},_}}) diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl index c941a80e61..9b414cade6 100644 --- a/lib/compiler/test/misc_SUITE.erl +++ b/lib/compiler/test/misc_SUITE.erl @@ -179,7 +179,7 @@ silly_coverage(Config) when is_list(Config) ->      ?line expect_error(fun() -> v3_life:module(BadKernel, []) end),      %% v3_codegen -    CodegenInput = {?MODULE,[{foo,0}],[],[{function,foo,0,[a|b],a,b}]}, +    CodegenInput = {?MODULE,[{foo,0}],[],[{function,foo,0,[a|b],a,b,[]}]},      ?line expect_error(fun() -> v3_codegen:module(CodegenInput, []) end),      %% beam_block @@ -187,7 +187,7 @@ silly_coverage(Config) when is_list(Config) ->  		  [{function,foo,0,2,  		    [{label,1},  		     {func_info,{atom,?MODULE},{atom,foo},0}, -		     {label,2}|non_proper_list],99}]}, +		     {label,2}|non_proper_list]}],99},      ?line expect_error(fun() -> beam_block:module(BlockInput, []) end),      %% beam_bool diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl index c6e0f8d85d..760cf17225 100644 --- a/lib/compiler/test/trycatch_SUITE.erl +++ b/lib/compiler/test/trycatch_SUITE.erl @@ -314,19 +314,19 @@ eclectic(Conf) when is_list(Conf) ->      V = {make_ref(),3.1415926535,[[]|{}]},      ?line {{value,{value,V},V},V} =   	eclectic_1({foo,{value,{value,V}}}, undefined, {value,V}), -    ?line {{'EXIT',{V,[{?MODULE,foo,1}|_]}},V} =  +    ?line {{'EXIT',{V,[{?MODULE,foo,1,_}|_]}},V} =  	eclectic_1({catch_foo,{error,V}}, undefined, {value,V}),      ?line {{error,{exit,V},{'EXIT',V}},V} =  	eclectic_1({foo,{error,{exit,V}}}, error, {value,V}),      ?line {{value,{value,V},V}, -	   {'EXIT',{badarith,[{?MODULE,my_add,2}|_]}}} = +	   {'EXIT',{badarith,[{?MODULE,my_add,2,_}|_]}}} =  	eclectic_1({foo,{value,{value,V}}}, undefined, {'add',{0,a}}),      ?line {{'EXIT',V},V} =  	eclectic_1({catch_foo,{exit,V}}, undefined, {throw,V}), -    ?line {{error,{'div',{1,0}},{'EXIT',{badarith,[{?MODULE,my_div,2}|_]}}}, +    ?line {{error,{'div',{1,0}},{'EXIT',{badarith,[{?MODULE,my_div,2,_}|_]}}},  	   {'EXIT',V}} =  	eclectic_1({foo,{error,{'div',{1,0}}}}, error, {exit,V}), -    ?line {{{error,V},{'EXIT',{V,[{?MODULE,foo,1}|_]}}}, +    ?line {{{error,V},{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},  	   {'EXIT',V}} =  	eclectic_1({catch_foo,{throw,{error,V}}}, undefined, {exit,V}),      %% @@ -336,15 +336,15 @@ eclectic(Conf) when is_list(Conf) ->  	eclectic_2({throw,{value,V}}, throw, {value,V}),      ?line {{caught,{'EXIT',V}},undefined} =  	eclectic_2({value,{value,V}}, undefined, {exit,V}), -    ?line {{caught,{'EXIT',{V,[{?MODULE,foo,1}|_]}}},undefined} = +    ?line {{caught,{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},undefined} =  	eclectic_2({error,{value,V}}, throw, {error,V}), -    ?line {{caught,{'EXIT',{badarg,[{erlang,abs,[V]}|_]}}},V} = +    ?line {{caught,{'EXIT',{badarg,[{erlang,abs,[V],_}|_]}}},V} =  	eclectic_2({value,{'abs',V}}, undefined, {value,V}), -    ?line {{caught,{'EXIT',{badarith,[{?MODULE,my_add,2}|_]}}},V} = +    ?line {{caught,{'EXIT',{badarith,[{?MODULE,my_add,2,_}|_]}}},V} =  	eclectic_2({exit,{'add',{0,a}}}, exit, {value,V}),      ?line {{caught,{'EXIT',V}},undefined} =  	eclectic_2({value,{error,V}}, undefined, {exit,V}), -    ?line {{caught,{'EXIT',{V,[{?MODULE,foo,1}|_]}}},undefined} = +    ?line {{caught,{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}},undefined} =  	eclectic_2({throw,{'div',{1,0}}}, throw, {error,V}),      ok. | 
