aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2011-08-25 11:25:29 +0200
committerBjörn Gustavsson <[email protected]>2011-08-25 11:25:29 +0200
commit142700357d3c76aa4c916f0cd5f915a947cbf94c (patch)
tree3af82388a86d708b3185fc142013a459c0201652 /lib/compiler/src
parent756a93ca2064b9e0eba3d82a7bd37aeae0f39be1 (diff)
parent26cceb7a0718182e74083b4ad044985e8f624ee2 (diff)
downloadotp-142700357d3c76aa4c916f0cd5f915a947cbf94c.tar.gz
otp-142700357d3c76aa4c916f0cd5f915a947cbf94c.tar.bz2
otp-142700357d3c76aa4c916f0cd5f915a947cbf94c.zip
Merge branch 'bjorn/line-numbers-in-exceptions/OTP-9468' into major
* bjorn/line-numbers-in-exceptions/OTP-9468: (51 commits) debugger: By default, only save non-tail-recursive calls debugger: Add line_number_SUITE debugger: Include line numbers in exceptions Update examples in the documentation to include line numbers Update documentation for erlang:raise/3 and erlang:get_stacktrace/0 beam_lib: Retain the "Line" chunk when stripping BEAM files erl: Add +L to suppress loading of line number information compiler: Add no_line_info for suppressing line/1 instructions exception_SUITE: Test line numbers in exceptions common_test: Use line numbers in exceptions common_test tests: Don't do detailed testing of the stack backtrace test_server: Refactor init_per_testcase/3 into two functions Implement process_info(Pid, current_{location,stacktrace}) beam_emu: Factor out saving of stack trace from save_stacktrace() compiler: Don't create filenames starting with "./" ops.tab: Remove line instructions before tail-recursive calls Lookup and include filenames and line numbers in exceptions Fix decrement of continuation pointers Refactor building of the exception stacktrace BEAM loader: Load the line table ...
Diffstat (limited to 'lib/compiler/src')
-rw-r--r--lib/compiler/src/beam_asm.erl61
-rw-r--r--lib/compiler/src/beam_block.erl35
-rw-r--r--lib/compiler/src/beam_bsm.erl9
-rw-r--r--lib/compiler/src/beam_clean.erl26
-rw-r--r--lib/compiler/src/beam_dead.erl10
-rw-r--r--lib/compiler/src/beam_dict.erl53
-rw-r--r--lib/compiler/src/beam_disasm.erl31
-rw-r--r--lib/compiler/src/beam_jump.erl49
-rw-r--r--lib/compiler/src/beam_listing.erl2
-rw-r--r--lib/compiler/src/beam_receive.erl2
-rw-r--r--lib/compiler/src/beam_trim.erl13
-rw-r--r--lib/compiler/src/beam_type.erl1
-rw-r--r--lib/compiler/src/beam_utils.erl16
-rw-r--r--lib/compiler/src/beam_validator.erl15
-rw-r--r--lib/compiler/src/compile.erl6
-rw-r--r--lib/compiler/src/genop.tab4
-rw-r--r--lib/compiler/src/v3_codegen.erl108
-rw-r--r--lib/compiler/src/v3_core.erl38
-rw-r--r--lib/compiler/src/v3_kernel.erl13
-rw-r--r--lib/compiler/src/v3_life.erl4
20 files changed, 372 insertions, 124 deletions
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 ce8a5bf864..29c7ec0dcd 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -171,9 +171,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) ->
@@ -1426,6 +1426,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/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(),