summaryrefslogtreecommitdiff
path: root/lib/compiler/src/beam_peep.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compiler/src/beam_peep.erl')
-rw-r--r--lib/compiler/src/beam_peep.erl223
1 files changed, 0 insertions, 223 deletions
diff --git a/lib/compiler/src/beam_peep.erl b/lib/compiler/src/beam_peep.erl
deleted file mode 100644
index da59aea2bd..0000000000
--- a/lib/compiler/src/beam_peep.erl
+++ /dev/null
@@ -1,223 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(beam_peep).
-
--export([module/2]).
-
--import(lists, [reverse/1,member/2,usort/1]).
-
--spec module(beam_utils:module_code(), [compile:option()]) ->
- {'ok',beam_utils:module_code()}.
-
-module({Mod,Exp,Attr,Fs0,_}, _Opts) ->
- %% First coalesce adjacent labels.
- {Fs1,Lc} = beam_clean:clean_labels(Fs0),
-
- %% Do the peep hole optimizations.
- Fs = [function(F) || F <- Fs1],
- {ok,{Mod,Exp,Attr,Fs,Lc}}.
-
-function({function,Name,Arity,CLabel,Is0}) ->
- try
- Is1 = peep(Is0),
- Is = beam_jump:remove_unused_labels(Is1),
- {function,Name,Arity,CLabel,Is}
- catch
- Class:Error:Stack ->
- io:fwrite("Function: ~w/~w\n", [Name,Arity]),
- erlang:raise(Class, Error, Stack)
- end.
-
-
-%% Peep-hole optimizations suitable to perform when most of the
-%% optimations passes have been run.
-%%
-%% (1) In a sequence of tests, we can remove any test instruction
-%% that has been previously seen, because it will certainly
-%% succeed.
-%%
-%% For instance, in the following code sequence
-%%
-%% is_eq_exact _Fail SomeRegister SomeLiteral
-%% is_ne_exact _Fail SomeOtherRegister SomeOtherLiteral
-%% is_eq_exact _Fail SomeRegister SomeLiteral
-%% is_ne_exact _Fail SomeOtherRegister StillSomeOtherLiteral
-%%
-%% the third test is redundant. The code sequence will be produced
-%% by a combination of semicolon and command guards, such as
-%%
-%% InEncoding =:= latin1, OutEncoding =:= unicode;
-%% InEncoding =:= latin1, OutEncoding =:= utf8 ->
-%%
-
-peep(Is) ->
- peep(Is, gb_sets:empty(), []).
-
-peep([{bif,tuple_size,_,[_]=Ops,Dst}=I|Is], SeenTests0, Acc) ->
- %% Pretend that we have seen {test,is_tuple,_,Ops}.
- SeenTests1 = gb_sets:add({is_tuple,Ops}, SeenTests0),
- %% Kill all remembered tests that depend on the destination register.
- SeenTests = kill_seen(Dst, SeenTests1),
- peep(Is, SeenTests, [I|Acc]);
-peep([{bif,map_get,_,[Key,Map],Dst}=I|Is], SeenTests0, Acc) ->
- %% Pretend that we have seen {test,has_map_fields,_,[Map,Key]}
- SeenTests1 = gb_sets:add({has_map_fields,[Map,Key]}, SeenTests0),
- %% Kill all remembered tests that depend on the destination register.
- SeenTests = kill_seen(Dst, SeenTests1),
- peep(Is, SeenTests, [I|Acc]);
-peep([{bif,_,_,_,Dst}=I|Is], SeenTests0, Acc) ->
- %% Kill all remembered tests that depend on the destination register.
- SeenTests = kill_seen(Dst, SeenTests0),
- peep(Is, SeenTests, [I|Acc]);
-peep([{gc_bif,_,_,_,_,Dst}=I|Is], SeenTests0, Acc) ->
- %% Kill all remembered tests that depend on the destination register.
- SeenTests = kill_seen(Dst, SeenTests0),
- peep(Is, SeenTests, [I|Acc]);
-peep([{jump,{f,L}},{label,L}=I|Is], _, Acc) ->
- %% Sometimes beam_jump has missed this optimization.
- peep(Is, gb_sets:empty(), [I|Acc]);
-peep([{select,select_val,R,F,Vls0}|Is], SeenTests0, Acc0) ->
- case prune_redundant_values(Vls0, F) of
- [] ->
- %% No values left. Must convert to plain jump.
- I = {jump,F},
- peep([I|Is], gb_sets:empty(), Acc0);
- [{atom,_}=Value,Lbl] ->
- %% Single value left. Convert to regular test.
- Is1 = [{test,is_eq_exact,F,[R,Value]},{jump,Lbl}|Is],
- peep(Is1, SeenTests0, Acc0);
- [{integer,_}=Value,Lbl] ->
- %% Single value left. Convert to regular test.
- Is1 = [{test,is_eq_exact,F,[R,Value]},{jump,Lbl}|Is],
- peep(Is1, SeenTests0, Acc0);
- [{atom,B1},Lbl,{atom,B2},Lbl] when B1 =:= not B2 ->
- %% Replace with is_boolean test.
- Is1 = [{test,is_boolean,F,[R]},{jump,Lbl}|Is],
- peep(Is1, SeenTests0, Acc0);
- [_|_]=Vls ->
- I = {select,select_val,R,F,Vls},
- peep(Is, gb_sets:empty(), [I|Acc0])
- end;
-peep([{get_map_elements,Fail,Src,List}=I|Is], _SeenTests, Acc0) ->
- SeenTests = gb_sets:empty(),
- case simplify_get_map_elements(Fail, Src, List, Acc0) of
- {ok,Acc} ->
- peep(Is, SeenTests, Acc);
- error ->
- peep(Is, SeenTests, [I|Acc0])
- end;
-peep([{test,has_map_fields,Fail,Ops}=I|Is], SeenTests, Acc0) ->
- case simplify_has_map_fields(Fail, Ops, Acc0) of
- {ok,Acc} ->
- peep(Is, SeenTests, Acc);
- error ->
- peep(Is, SeenTests, [I|Acc0])
- end;
-peep([{test,Op,_,Ops}=I|Is], SeenTests0, Acc) ->
- case beam_utils:is_pure_test(I) of
- false ->
- %% Bit syntax matching, which may modify registers and/or
- %% match state. Clear all information about tests that
- %% has succeeded.
- peep(Is, gb_sets:empty(), [I|Acc]);
- true ->
- case is_test_redundant(Op, Ops, SeenTests0) of
- true ->
- %% This test or a similar test has already succeeded and
- %% is therefore redundant.
- peep(Is, SeenTests0, Acc);
- false ->
- %% Remember that we have seen this test.
- Test = {Op,Ops},
- SeenTests = gb_sets:insert(Test, SeenTests0),
- peep(Is, SeenTests, [I|Acc])
- end
- end;
-peep([I|Is], _, Acc) ->
- %% An unknown instruction. Throw away all information we
- %% have collected about test instructions.
- peep(Is, gb_sets:empty(), [I|Acc]);
-peep([], _, Acc) -> reverse(Acc).
-
-is_test_redundant(Op, Ops, Seen) ->
- gb_sets:is_element({Op,Ops}, Seen) orelse
- is_test_redundant_1(Op, Ops, Seen).
-
-is_test_redundant_1(is_boolean, [R], Seen) ->
- gb_sets:is_element({is_eq_exact,[R,{atom,false}]}, Seen) orelse
- gb_sets:is_element({is_eq_exact,[R,{atom,true}]}, Seen);
-is_test_redundant_1(_, _, _) -> false.
-
-kill_seen(Dst, Seen0) ->
- gb_sets:from_ordset(kill_seen_1(gb_sets:to_list(Seen0), Dst)).
-
-kill_seen_1([{_,Ops}=Test|T], Dst) ->
- case member(Dst, Ops) of
- true -> kill_seen_1(T, Dst);
- false -> [Test|kill_seen_1(T, Dst)]
- end;
-kill_seen_1([], _) -> [].
-
-prune_redundant_values([_Val,F|Vls], F) ->
- prune_redundant_values(Vls, F);
-prune_redundant_values([Val,Lbl|Vls], F) ->
- [Val,Lbl|prune_redundant_values(Vls, F)];
-prune_redundant_values([], _) -> [].
-
-simplify_get_map_elements(Fail, Src, {list,[Key,Dst]},
- [{get_map_elements,Fail,Src,{list,List1}}|Acc]) ->
- case are_keys_literals([Key]) andalso are_keys_literals(List1) andalso
- not is_source_overwritten(Src, List1) of
- true ->
- case member(Key, List1) of
- true ->
- %% The key is already in the other list. That is
- %% very unusual, because there are optimizations to get
- %% rid of duplicate keys. Therefore, don't try to
- %% do anything smart here; just keep the
- %% get_map_elements instructions separate.
- error;
- false ->
- List = [Key,Dst|List1],
- {ok,[{get_map_elements,Fail,Src,{list,List}}|Acc]}
- end;
- false ->
- error
- end;
-simplify_get_map_elements(_, _, _, _) -> error.
-
-simplify_has_map_fields(Fail, [Src|Keys0],
- [{test,has_map_fields,Fail,[Src|Keys1]}|Acc]) ->
- case are_keys_literals(Keys0) andalso are_keys_literals(Keys1) of
- true ->
- Keys = usort(Keys0 ++ Keys1),
- {ok,[{test,has_map_fields,Fail,[Src|Keys]}|Acc]};
- false ->
- error
- end;
-simplify_has_map_fields(_, _, _) -> error.
-
-are_keys_literals([{x,_}|_]) -> false;
-are_keys_literals([{y,_}|_]) -> false;
-are_keys_literals([_|_]) -> true.
-
-is_source_overwritten(Src, [_Key,Src]) -> true;
-is_source_overwritten(_, _) -> false.