aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src')
-rw-r--r--lib/compiler/src/beam_block.erl23
-rw-r--r--lib/compiler/src/beam_clean.erl21
-rw-r--r--lib/compiler/src/beam_flatten.erl3
-rw-r--r--lib/compiler/src/beam_split.erl7
-rw-r--r--lib/compiler/src/beam_utils.erl81
-rw-r--r--lib/compiler/src/beam_validator.erl24
6 files changed, 105 insertions, 54 deletions
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 7183381334..47a2be8ab5 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -36,13 +36,11 @@ module({Mod,Exp,Attr,Fs0,Lc}, Opts) ->
function({function,Name,Arity,CLabel,Is0}, Blockify) ->
try
%% Collect basic blocks and optimize them.
- Is2 = case Blockify of
- true ->
- Is1 = blockify(Is0),
- embed_lines(Is1);
- false ->
- Is0
+ Is1 = case Blockify of
+ false -> Is0;
+ true -> blockify(Is0)
end,
+ Is2 = embed_lines(Is1),
Is3 = local_cse(Is2),
Is4 = beam_utils:anno_defs(Is3),
Is5 = move_allocates(Is4),
@@ -139,6 +137,11 @@ embed_lines([{block,B2},{line,_}=Line,{block,B1}|T], Acc) ->
embed_lines([{block,B1},{line,_}=Line|T], Acc) ->
B = {block,[{set,[],[],Line}|B1]},
embed_lines([B|T], Acc);
+embed_lines([{block,B2},{block,B1}|T], Acc) ->
+ %% This can only happen when beam_block is run for
+ %% the second time.
+ B = {block,B1++B2},
+ embed_lines([B|T], Acc);
embed_lines([I|Is], Acc) ->
embed_lines(Is, [I|Acc]);
embed_lines([], Acc) -> Acc.
@@ -628,7 +631,13 @@ cse_find(Expr, Es) ->
end.
cse_expr({set,[D],Ss,{bif,N,_}}) ->
- {ok,D,{{bif,N},Ss}};
+ case D of
+ {fr,_} ->
+ %% There are too many things that can go wrong.
+ none;
+ _ ->
+ {ok,D,{{bif,N},Ss}}
+ end;
cse_expr({set,[D],Ss,{alloc,_,{gc_bif,N,_}}}) ->
{ok,D,{{gc_bif,N},Ss}};
cse_expr({set,[D],Ss,put_list}) ->
diff --git a/lib/compiler/src/beam_clean.erl b/lib/compiler/src/beam_clean.erl
index e094c2c320..7ddf9fa2e2 100644
--- a/lib/compiler/src/beam_clean.erl
+++ b/lib/compiler/src/beam_clean.erl
@@ -24,7 +24,7 @@
-export([module/2]).
-export([bs_clean_saves/1]).
-export([clean_labels/1]).
--import(lists, [foldl/3,reverse/1,filter/2]).
+-import(lists, [foldl/3,reverse/1]).
-spec module(beam_utils:module_code(), [compile:option()]) ->
{'ok',beam_utils:module_code()}.
@@ -303,8 +303,21 @@ maybe_remove_lines(Fs, Opts) ->
end.
remove_lines([{function,N,A,Lbl,Is0}|T]) ->
- Is = filter(fun({line,_}) -> false;
- (_) -> true
- end, Is0),
+ Is = remove_lines_fun(Is0),
[{function,N,A,Lbl,Is}|remove_lines(T)];
remove_lines([]) -> [].
+
+remove_lines_fun([{line,_}|Is]) ->
+ remove_lines_fun(Is);
+remove_lines_fun([{block,Bl0}|Is]) ->
+ Bl = remove_lines_block(Bl0),
+ [{block,Bl}|remove_lines_fun(Is)];
+remove_lines_fun([I|Is]) ->
+ [I|remove_lines_fun(Is)];
+remove_lines_fun([]) -> [].
+
+remove_lines_block([{set,_,_,{line,_}}|Is]) ->
+ remove_lines_block(Is);
+remove_lines_block([I|Is]) ->
+ [I|remove_lines_block(Is)];
+remove_lines_block([]) -> [].
diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl
index 4045ab6dc5..c60211f516 100644
--- a/lib/compiler/src/beam_flatten.erl
+++ b/lib/compiler/src/beam_flatten.erl
@@ -73,7 +73,8 @@ norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
{put_map,F,Op,S,D,R,{list,Puts}};
norm({set,[],[],remove_message}) -> remove_message;
norm({set,[],[],fclearerror}) -> fclearerror;
-norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}}.
+norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}};
+norm({set,[],[],{line,_}=Line}) -> Line.
norm_allocate({_Zero,nostack,Nh,[]}, Regs) ->
[{test_heap,Nh,Regs}];
diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl
index d041f18806..52dd89b5bb 100644
--- a/lib/compiler/src/beam_split.erl
+++ b/lib/compiler/src/beam_split.erl
@@ -50,8 +50,9 @@ split_block([{set,[R],[_,_,_]=As,{bif,is_record,{f,Lbl}}}|Is], Bl, Acc) ->
split_block(Is, [], [{bif,is_record,{f,Lbl},As,R}|make_block(Bl, Acc)]);
split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 ->
split_block(Is, [], [{bif,N,Fail,As,R}|make_block(Bl, Acc)]);
-split_block([{set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) ->
- split_block(Is, [], [{bif,raise,Fail,As,R}|make_block(Bl, Acc)]);
+split_block([{set,[],[],{line,_}=Line},
+ {set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) ->
+ split_block(Is, [], [{bif,raise,Fail,As,R},Line|make_block(Bl, Acc)]);
split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc)
when Lbl =/= 0 ->
split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]);
@@ -61,8 +62,6 @@ split_block([{set,[D],[S|Puts],{alloc,R,{put_map,Op,{f,Lbl}=Fail}}}|Is],
make_block(Bl, Acc)]);
split_block([{set,[R],[],{try_catch,Op,L}}|Is], Bl, Acc) ->
split_block(Is, [], [{Op,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).
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index a57dbbbc2f..814cfb8265 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -118,7 +118,7 @@ is_killed(R, Is, D) ->
St = #live{lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{killed,_} -> true;
- {exit_not_used,_} -> true;
+ {exit_not_used,_} -> false;
{_,_} -> false
end.
@@ -131,7 +131,7 @@ is_killed_at(R, Lbl, D) when is_integer(Lbl) ->
St0 = #live{lbl=D,res=gb_trees:empty()},
case check_liveness_at(R, Lbl, St0) of
{killed,_} -> true;
- {exit_not_used,_} -> true;
+ {exit_not_used,_} -> false;
{_,_} -> false
end.
@@ -148,7 +148,7 @@ is_not_used(R, Is, D) ->
St = #live{lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{used,_} -> false;
- {exit_not_used,_} -> false;
+ {exit_not_used,_} -> true;
{_,_} -> true
end.
@@ -1004,47 +1004,52 @@ live_opt([{recv_mark,_}=I|Is], Regs, D, Acc) ->
live_opt([], _, _, Acc) -> Acc.
-live_opt_block([{set,Ds,Ss,Op0}|Is], Regs0, D, Acc) ->
- Regs1 = x_live(Ss, x_dead(Ds, Regs0)),
- {Op, Regs} = live_opt_block_op(Op0, Regs1, D),
- I = {set, Ds, Ss, Op},
-
- case Ds of
- [{x,X}] ->
- case (not is_live(X, Regs0)) andalso Op =:= move of
- true ->
- live_opt_block(Is, Regs0, D, Acc);
- false ->
- live_opt_block(Is, Regs, D, [I|Acc])
- end;
- _ ->
- live_opt_block(Is, Regs, D, [I|Acc])
+live_opt_block([{set,[{x,X}]=Ds,Ss,move}=I|Is], Regs0, D, Acc) ->
+ Regs = x_live(Ss, x_dead(Ds, Regs0)),
+ case is_live(X, Regs0) of
+ true ->
+ live_opt_block(Is, Regs, D, [I|Acc]);
+ false ->
+ %% Useless move, will never be used.
+ live_opt_block(Is, Regs, D, Acc)
end;
-live_opt_block([{'%anno',_}|Is], Regs, D, Acc) ->
- live_opt_block(Is, Regs, D, Acc);
-live_opt_block([], Regs, _, Acc) -> {Acc,Regs}.
-
-live_opt_block_op({alloc,Live0,AllocOp}, Regs0, D) ->
- Regs =
- case AllocOp of
- {Kind, _N, Fail} when Kind =:= gc_bif; Kind =:= put_map ->
- live_join_label(Fail, D, Regs0);
- _ ->
- Regs0
- end,
+live_opt_block([{set,Ds,Ss,{alloc,Live0,AllocOp}}|Is], Regs0, D, Acc) ->
+ %% Calculate liveness from the point of view of the GC.
+ %% There will never be a GC if the instruction fails, so we should
+ %% ignore the failure branch.
+ GcRegs1 = x_dead(Ds, Regs0),
+ GcRegs = x_live(Ss, GcRegs1),
+ Live = live_regs(GcRegs),
%% The life-time analysis used by the code generator is sometimes too
%% conservative, so it may be possible to lower the number of live
%% registers based on the exact liveness information. The main benefit is
%% that more optimizations that depend on liveness information (such as the
- %% beam_bool and beam_dead passes) may be applied.
- Live = live_regs(Regs),
- true = Live =< Live0,
- {{alloc,Live,AllocOp}, live_call(Live)};
-live_opt_block_op({bif,_N,Fail} = Op, Regs, D) ->
- {Op, live_join_label(Fail, D, Regs)};
-live_opt_block_op(Op, Regs, _D) ->
- {Op, Regs}.
+ %% beam_dead pass) may be applied.
+ true = Live =< Live0, %Assertion.
+ I = {set,Ds,Ss,{alloc,Live,AllocOp}},
+
+ %% Calculate liveness from the point of view of the preceding instruction.
+ %% The liveness is the union of live registers in the GC and the live
+ %% registers at the failure label.
+ Regs1 = live_call(Live),
+ Regs = live_join_alloc(AllocOp, D, Regs1),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{set,Ds,Ss,{bif,_,Fail}}=I|Is], Regs0, D, Acc) ->
+ Regs1 = x_dead(Ds, Regs0),
+ Regs2 = x_live(Ss, Regs1),
+ Regs = live_join_label(Fail, D, Regs2),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{set,Ds,Ss,_}=I|Is], Regs0, D, Acc) ->
+ Regs = x_live(Ss, x_dead(Ds, Regs0)),
+ live_opt_block(Is, Regs, D, [I|Acc]);
+live_opt_block([{'%anno',_}|Is], Regs, D, Acc) ->
+ live_opt_block(Is, Regs, D, Acc);
+live_opt_block([], Regs, _, Acc) -> {Acc,Regs}.
+
+live_join_alloc({Kind,_Name,Fail}, D, Regs) when Kind =:= gc_bif; Kind =:= put_map ->
+ live_join_label(Fail, D, Regs);
+live_join_alloc(_, _, Regs) -> Regs.
live_join_labels([{f,L}|T], D, Regs0) when L =/= 0 ->
Regs = gb_trees:get(L, D) bor Regs0,
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 9de773542e..c30ab34ac7 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -1153,6 +1153,7 @@ set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys0}=St}=Vst)
{value,_} ->
gb_trees:update(Y, Type, Ys0)
end,
+ check_try_catch_tags(Type, Y, Ys0),
Vst#vst{current=St#st{y=Ys}};
set_type_y(Type, Reg, #vst{}) -> error({invalid_store,Reg,Type}).
@@ -1160,6 +1161,29 @@ set_catch_end({y,Y}, #vst{current=#st{y=Ys0}=St}=Vst) ->
Ys = gb_trees:update(Y, initialized, Ys0),
Vst#vst{current=St#st{y=Ys}}.
+check_try_catch_tags(Type, LastY, Ys) ->
+ case is_try_catch_tag(Type) of
+ false ->
+ ok;
+ true ->
+ %% Every catch or try/catch must use a lower Y register
+ %% number than any enclosing catch or try/catch. That will
+ %% ensure that when the stack is scanned when an
+ %% exception occurs, the innermost try/catch tag is found
+ %% first.
+ Bad = [{{y,Y},Tag} || {Y,Tag} <- gb_trees:to_list(Ys),
+ Y < LastY, is_try_catch_tag(Tag)],
+ case Bad of
+ [] ->
+ ok;
+ [_|_] ->
+ error({bad_try_catch_nesting,{y,LastY},Bad})
+ end
+ end.
+
+is_try_catch_tag({catchtag,_}) -> true;
+is_try_catch_tag({trytag,_}) -> true;
+is_try_catch_tag(_) -> false.
is_reg_defined({x,_}=Reg, Vst) -> is_type_defined_x(Reg, Vst);
is_reg_defined({y,_}=Reg, Vst) -> is_type_defined_y(Reg, Vst);