Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor cleanups and bug fixes of the compiler #1965

Merged
merged 9 commits into from Oct 1, 2018
3 changes: 3 additions & 0 deletions lib/compiler/src/beam_a.erl
Expand Up @@ -59,6 +59,9 @@ rename_instrs([{test,is_eq_exact,_,[Dst,Src]}=Test,
rename_instrs([{test,is_eq_exact,_,[Same,Same]}|Is]) ->
%% Same literal or same register. Will always succeed.
rename_instrs(Is);
rename_instrs([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_},{label,Fail}|Is]) ->
%% This instruction sequence does nothing.
rename_instrs(Is);
rename_instrs([{apply_last,A,N}|Is]) ->
[{apply,A},{deallocate,N},return|rename_instrs(Is)];
rename_instrs([{call_last,A,F,N}|Is]) ->
Expand Down
3 changes: 0 additions & 3 deletions lib/compiler/src/beam_block.erl
Expand Up @@ -49,9 +49,6 @@ function({function,Name,Arity,CLabel,Is0}) ->
blockify(Is) ->
blockify(Is, []).

blockify([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_Lbl},{label,Fail}|Is], Acc) ->
%% Useless instruction sequence.
blockify(Is, Acc);
blockify([I|Is0]=IsAll, Acc) ->
case collect(I) of
error -> blockify(Is0, [I|Acc]);
Expand Down
18 changes: 8 additions & 10 deletions lib/compiler/src/beam_clean.erl
Expand Up @@ -23,17 +23,15 @@

-export([module/2]).
-export([clean_labels/1]).
-import(lists, [foldl/3]).

-spec module(beam_utils:module_code(), [compile:option()]) ->
{'ok',beam_utils:module_code()}.

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),
All = maps:from_list([{Lbl,Func} || {function,_,_,Lbl,_}=Func <- Fs0]),
WorkList = rootset(Fs0, Exp, Attr),
Used = find_all_used(WorkList, All, sets:from_list(WorkList)),
Used = find_all_used(WorkList, All, cerl_sets:from_list(WorkList)),
Fs1 = remove_unused(Order, Used, All),
{Fs2,Lc} = clean_labels(Fs1),
Fs = maybe_remove_lines(Fs2, Opts),
Expand All @@ -55,16 +53,16 @@ rootset(Fs, Root0, Attr) ->
%% Remove the unused functions.

remove_unused([F|Fs], Used, All) ->
case sets:is_element(F, Used) of
case cerl_sets:is_element(F, Used) of
false -> remove_unused(Fs, Used, All);
true -> [dict:fetch(F, All)|remove_unused(Fs, Used, All)]
true -> [map_get(F, All)|remove_unused(Fs, Used, All)]
end;
remove_unused([], _, _) -> [].

%% Find all used functions.

find_all_used([F|Fs0], All, Used0) ->
{function,_,_,_,Code} = dict:fetch(F, All),
{function,_,_,_,Code} = map_get(F, All),
{Fs,Used} = update_work_list(Code, {Fs0,Used0}),
find_all_used(Fs, All, Used);
find_all_used([], _All, Used) -> Used.
Expand All @@ -78,9 +76,9 @@ update_work_list([_|Is], Sets) ->
update_work_list([], Sets) -> Sets.

add_to_work_list(F, {Fs,Used}=Sets) ->
case sets:is_element(F, Used) of
case cerl_sets:is_element(F, Used) of
true -> Sets;
false -> {[F|Fs],sets:add_element(F, Used)}
false -> {[F|Fs],cerl_sets:add_element(F, Used)}
end.


Expand Down
72 changes: 4 additions & 68 deletions lib/compiler/src/beam_flatten.erl
Expand Up @@ -32,8 +32,7 @@ module({Mod,Exp,Attr,Fs,Lc}, _Opt) ->
{ok,{Mod,Exp,Attr,[function(F) || F <- Fs],Lc}}.

function({function,Name,Arity,CLabel,Is0}) ->
Is1 = block(Is0),
Is = opt(Is1),
Is = block(Is0),
{function,Name,Arity,CLabel,Is}.

block(Is) ->
Expand All @@ -43,21 +42,12 @@ block([{block,Is0}|Is1], Acc) -> block(Is1, norm_block(Is0, Acc));
block([I|Is], Acc) -> block(Is, [I|Acc]);
block([], Acc) -> reverse(Acc).

norm_block([{set,[],[],{alloc,R,{_,nostack,_,_}=Alloc}}|Is], Acc0) ->
case insert_alloc_in_bs_init(Acc0, Alloc) of
impossible ->
norm_block(Is, reverse(norm_allocate(Alloc, R), Acc0));
Acc ->
norm_block(Is, Acc)
end;
norm_block([{set,[],[],{alloc,R,Alloc}}|Is], Acc0) ->
norm_block(Is, reverse(norm_allocate(Alloc, R), Acc0));
norm_block([{set,[D1],[S],get_hd},{set,[D2],[S],get_tl}|Is], Acc) ->
I = {get_list,S,D1,D2},
norm_block(Is, [I|Acc]);
norm_block([I|Is], Acc) -> norm_block(Is, [norm(I)|Acc]);
norm_block([I|Is], Acc) ->
norm_block(Is, [norm(I)|Acc]);
norm_block([], Acc) -> Acc.

norm({set,[D],As,{bif,N,F}}) -> {bif,N,F,As,D};
norm({set,[D],As,{alloc,R,{gc_bif,N,F}}}) -> {gc_bif,N,F,R,As,D};
norm({set,[D],[],init}) -> {init,D};
Expand Down Expand Up @@ -91,57 +81,3 @@ norm_allocate({nozero,Ns,0,Inits}, Regs) ->
[{allocate,Ns,Regs}|Inits];
norm_allocate({nozero,Ns,Nh,Inits}, Regs) ->
[{allocate_heap,Ns,Nh,Regs}|Inits].

%% insert_alloc_in_bs_init(ReverseInstructionStream, AllocationInfo) ->
%% impossible | ReverseInstructionStream'
%% A bs_init/6 instruction should not be followed by a test heap instruction.
%% Given the AllocationInfo from a test heap instruction, merge the
%% allocation amounts into the previous bs_init/6 instruction (if any).
%%
insert_alloc_in_bs_init([{bs_put,_,_,_}=I|Is], Alloc) ->
%% The instruction sequence ends with an bs_put/4 instruction.
%% We'll need to search backwards for the bs_init/6 instruction.
insert_alloc_1(Is, Alloc, [I]);
insert_alloc_in_bs_init(_, _) -> impossible.

insert_alloc_1([{bs_init=Op,Fail,Info0,Live,Ss,Dst}|Is],
{_,nostack,Ws2,[]}, Acc) when is_integer(Live) ->
%% The number of extra heap words is always in the second position
%% in the Info tuple.
Ws1 = element(2, Info0),
Al = beam_utils:combine_heap_needs(Ws1, Ws2),
Info = setelement(2, Info0, Al),
I = {Op,Fail,Info,Live,Ss,Dst},
reverse(Acc, [I|Is]);
insert_alloc_1([{bs_put,_,_,_}=I|Is], Alloc, Acc) ->
insert_alloc_1(Is, Alloc, [I|Acc]).

%% opt(Is0) -> Is
%% Simple peep-hole optimization to move a {move,Any,{x,0}} past
%% any kill up to the next call instruction. (To give the loader
%% an opportunity to combine the 'move' and the 'call' instructions.)
%%
opt(Is) ->
opt_1(Is, []).

opt_1([{move,_,{x,0}}=I|Is0], Acc0) ->
case move_past_kill(Is0, I, Acc0) of
impossible -> opt_1(Is0, [I|Acc0]);
{Is,Acc} -> opt_1(Is, Acc)
end;
opt_1([I|Is], Acc) ->
opt_1(Is, [I|Acc]);
opt_1([], Acc) -> reverse(Acc).

move_past_kill([{kill,Src}|_], {move,Src,_}, _) ->
impossible;
move_past_kill([{kill,_}=I|Is], Move, Acc) ->
move_past_kill(Is, Move, [I|Acc]);
move_past_kill([{trim,N,_}=I|Is], {move,Src,Dst}=Move, Acc) ->
case Src of
{y,Y} when Y < N-> impossible;
{y,Y} -> {Is,[{move,{y,Y-N},Dst},I|Acc]};
_ -> {Is,[Move,I|Acc]}
end;
move_past_kill(Is, Move, Acc) ->
{Is,[Move|Acc]}.